Step and field conditionality (#87)
* Re structure builder logic to allow for step conditionality Concerns - Performance. Look at whether the additional build in the steps controller can be reduced - Does not work if applied to the last step. - Certain conditions will not work with the first step(?) - How should this be scoped to known functionality? * Add indexes and conditions to steps and fields * Complete and add spec * Complete backend * Complete step conditionality and field indexing * Fix failing spec * Update coverage * Apply rubocop * Apply prettier * Apply prettier to wizard js * Fix schema issues created in merge * Remove setting label for force_final * Improve client wizard cache naming * Improve steps controller and spec conditionality * Improve final step attribute naming * Fix failing spec * Linting * Add one more final step test * Linting * Fix eslint issues * Apply prettier * Linting, syntax, merge and copy cleanups * Update wizard-admin.scss * Fix template linting * Rubocop fixes
Dieser Commit ist enthalten in:
Ursprung
ec21c8e274
Commit
ceef3f4bc9
70 geänderte Dateien mit 1387 neuen und 573 gelöschten Zeilen
|
@ -107,6 +107,40 @@ export default Component.extend(UndoChanges, {
|
|||
return this.setupTypeOutput(fieldType, options);
|
||||
},
|
||||
|
||||
@discourseComputed("step.index")
|
||||
fieldConditionOptions(stepIndex) {
|
||||
const options = {
|
||||
inputTypes: "validation",
|
||||
context: "field",
|
||||
textSelection: "value",
|
||||
userFieldSelection: true,
|
||||
groupSelection: true,
|
||||
};
|
||||
|
||||
if (stepIndex > 0) {
|
||||
options.wizardFieldSelection = true;
|
||||
options.wizardActionSelection = true;
|
||||
}
|
||||
|
||||
return options;
|
||||
},
|
||||
|
||||
@discourseComputed("step.index")
|
||||
fieldIndexOptions(stepIndex) {
|
||||
const options = {
|
||||
context: "field",
|
||||
userFieldSelection: true,
|
||||
groupSelection: true,
|
||||
};
|
||||
|
||||
if (stepIndex > 0) {
|
||||
options.wizardFieldSelection = true;
|
||||
options.wizardActionSelection = true;
|
||||
}
|
||||
|
||||
return options;
|
||||
},
|
||||
|
||||
actions: {
|
||||
imageUploadDone(upload) {
|
||||
this.set("field.image", upload.url);
|
||||
|
|
|
@ -1,8 +1,27 @@
|
|||
import Component from "@ember/component";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default Component.extend({
|
||||
classNames: "wizard-custom-step",
|
||||
|
||||
@discourseComputed("step.index")
|
||||
stepConditionOptions(stepIndex) {
|
||||
const options = {
|
||||
inputTypes: "validation",
|
||||
context: "step",
|
||||
textSelection: "value",
|
||||
userFieldSelection: true,
|
||||
groupSelection: true,
|
||||
};
|
||||
|
||||
if (stepIndex > 0) {
|
||||
options["wizardFieldSelection"] = true;
|
||||
options["wizardActionSelection"] = true;
|
||||
}
|
||||
|
||||
return options;
|
||||
},
|
||||
|
||||
actions: {
|
||||
bannerUploadDone(upload) {
|
||||
this.set("step.banner", upload.url);
|
||||
|
|
|
@ -38,6 +38,7 @@ export default Component.extend({
|
|||
const items = this.items;
|
||||
const item = items.findBy("id", itemId);
|
||||
items.removeObject(item);
|
||||
item.set("index", newIndex);
|
||||
items.insertAt(newIndex, item);
|
||||
scheduleOnce("afterRender", this, () => this.applySortable());
|
||||
},
|
||||
|
@ -90,22 +91,14 @@ export default Component.extend({
|
|||
|
||||
params.isNew = true;
|
||||
|
||||
let next = 1;
|
||||
|
||||
let index = 0;
|
||||
if (items.length) {
|
||||
next =
|
||||
Math.max.apply(
|
||||
Math,
|
||||
items.map((i) => {
|
||||
let parts = i.id.split("_");
|
||||
let lastPart = parts[parts.length - 1];
|
||||
return isNaN(lastPart) ? 0 : lastPart;
|
||||
})
|
||||
) + 1;
|
||||
index = items.length;
|
||||
}
|
||||
|
||||
let id = `${itemType}_${next}`;
|
||||
params.index = index;
|
||||
|
||||
let id = `${itemType}_${index + 1}`;
|
||||
if (itemType === "field") {
|
||||
id = `${this.parentId}_${id}`;
|
||||
}
|
||||
|
|
|
@ -23,10 +23,7 @@ function castCase(property, value) {
|
|||
return property.indexOf("_type") > -1 ? camelCase(value) : value;
|
||||
}
|
||||
|
||||
function buildProperty(json, property, type) {
|
||||
let value = json[property];
|
||||
|
||||
if (mapped(property, type) && present(value) && value.constructor === Array) {
|
||||
function buildMappedProperty(value) {
|
||||
let inputs = [];
|
||||
|
||||
value.forEach((inputJson) => {
|
||||
|
@ -59,18 +56,36 @@ function buildProperty(json, property, type) {
|
|||
});
|
||||
|
||||
return A(inputs);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
function buildObject(json, type) {
|
||||
function buildProperty(json, property, type, objectIndex) {
|
||||
let value = json[property];
|
||||
if (
|
||||
property === "index" &&
|
||||
(value === null || value === undefined) &&
|
||||
(objectIndex !== null || objectIndex !== undefined)
|
||||
) {
|
||||
return objectIndex;
|
||||
}
|
||||
|
||||
if (
|
||||
!mapped(property, type) ||
|
||||
!present(value) ||
|
||||
!value.constructor === Array
|
||||
) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return buildMappedProperty(value);
|
||||
}
|
||||
|
||||
function buildObject(json, type, objectIndex) {
|
||||
let props = {
|
||||
isNew: false,
|
||||
};
|
||||
|
||||
Object.keys(json).forEach((prop) => {
|
||||
props[prop] = buildProperty(json, prop, type);
|
||||
props[prop] = buildProperty(json, prop, type, objectIndex);
|
||||
});
|
||||
|
||||
return EmberObject.create(props);
|
||||
|
@ -80,8 +95,8 @@ function buildObjectArray(json, type) {
|
|||
let array = A();
|
||||
|
||||
if (present(json)) {
|
||||
json.forEach((objJson) => {
|
||||
let object = buildObject(objJson, type);
|
||||
json.forEach((objJson, objectIndex) => {
|
||||
let object = buildObject(objJson, type, objectIndex);
|
||||
|
||||
if (hasAdvancedProperties(object, type)) {
|
||||
object.set("showAdvanced", true);
|
||||
|
@ -94,9 +109,9 @@ function buildObjectArray(json, type) {
|
|||
return array;
|
||||
}
|
||||
|
||||
function buildBasicProperties(json, type, props) {
|
||||
function buildBasicProperties(json, type, props, objectIndex = null) {
|
||||
listProperties(type).forEach((p) => {
|
||||
props[p] = buildProperty(json, p, type);
|
||||
props[p] = buildProperty(json, p, type, objectIndex);
|
||||
|
||||
if (hasAdvancedProperties(json, type)) {
|
||||
props.showAdvanced = true;
|
||||
|
@ -142,12 +157,17 @@ function buildProperties(json) {
|
|||
props = buildBasicProperties(json, "wizard", props);
|
||||
|
||||
if (present(json.steps)) {
|
||||
json.steps.forEach((stepJson) => {
|
||||
json.steps.forEach((stepJson, objectIndex) => {
|
||||
let stepProps = {
|
||||
isNew: false,
|
||||
};
|
||||
|
||||
stepProps = buildBasicProperties(stepJson, "step", stepProps);
|
||||
stepProps = buildBasicProperties(
|
||||
stepJson,
|
||||
"step",
|
||||
stepProps,
|
||||
objectIndex
|
||||
);
|
||||
stepProps.fields = buildObjectArray(stepJson.fields, "field");
|
||||
|
||||
props.steps.pushObject(EmberObject.create(stepProps));
|
||||
|
|
|
@ -37,6 +37,7 @@ const wizard = {
|
|||
const step = {
|
||||
basic: {
|
||||
id: null,
|
||||
index: null,
|
||||
title: null,
|
||||
key: null,
|
||||
banner: null,
|
||||
|
@ -44,9 +45,11 @@ const step = {
|
|||
required_data: null,
|
||||
required_data_message: null,
|
||||
permitted_params: null,
|
||||
condition: null,
|
||||
force_final: false,
|
||||
},
|
||||
mapped: ["required_data", "permitted_params"],
|
||||
advanced: ["required_data", "permitted_params"],
|
||||
mapped: ["required_data", "permitted_params", "condition", "index"],
|
||||
advanced: ["required_data", "permitted_params", "condition", "index"],
|
||||
required: ["id"],
|
||||
dependent: {},
|
||||
objectArrays: {
|
||||
|
@ -60,16 +63,18 @@ const step = {
|
|||
const field = {
|
||||
basic: {
|
||||
id: null,
|
||||
index: null,
|
||||
label: null,
|
||||
image: null,
|
||||
description: null,
|
||||
required: null,
|
||||
key: null,
|
||||
type: null,
|
||||
condition: null,
|
||||
},
|
||||
types: {},
|
||||
mapped: ["prefill", "content"],
|
||||
advanced: ["property", "key"],
|
||||
mapped: ["prefill", "content", "condition", "index"],
|
||||
advanced: ["property", "key", "condition", "index"],
|
||||
required: ["id", "type"],
|
||||
dependent: {},
|
||||
objectArrays: {},
|
||||
|
|
|
@ -131,11 +131,15 @@ const CustomWizard = EmberObject.extend({
|
|||
return result;
|
||||
},
|
||||
|
||||
buildMappedJson(inputs) {
|
||||
if (!inputs || !inputs.length) {
|
||||
buildMappedJson(value) {
|
||||
if (typeof value === "string" || Number.isInteger(value)) {
|
||||
return value;
|
||||
}
|
||||
if (!value || !value.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let inputs = value;
|
||||
let result = [];
|
||||
|
||||
inputs.forEach((inpt) => {
|
||||
|
|
|
@ -188,6 +188,30 @@
|
|||
{{#if field.showAdvanced}}
|
||||
<div class="advanced-settings">
|
||||
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.condition"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=field.condition
|
||||
options=fieldConditionOptions}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.index"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=field.index
|
||||
options=fieldIndexOptions}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if isCategory}}
|
||||
<div class="setting">
|
||||
<div class="setting-label">
|
||||
|
|
|
@ -38,6 +38,27 @@
|
|||
{{#if step.showAdvanced}}
|
||||
<div class="advanced-settings">
|
||||
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.condition"}}</label>
|
||||
</div>
|
||||
|
||||
<div class="setting-value">
|
||||
{{wizard-mapper
|
||||
inputs=step.condition
|
||||
options=stepConditionOptions}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting full">
|
||||
<div class="setting-label"></div>
|
||||
<div class="setting-value force-final">
|
||||
<h4>{{i18n "admin.wizard.step.force_final.label"}}</h4>
|
||||
{{input type="checkbox" checked=step.force_final}}
|
||||
<span>{{i18n "admin.wizard.step.force_final.description"}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting full field-mapper-setting">
|
||||
<div class="setting-label">
|
||||
<label>{{i18n "admin.wizard.step.required_data.label"}}</label>
|
||||
|
@ -92,7 +113,6 @@
|
|||
placeholderKey="admin.wizard.translation_placeholder"}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
|
@ -105,6 +125,7 @@
|
|||
{{#each step.fields as |field|}}
|
||||
{{wizard-custom-field
|
||||
field=field
|
||||
step=step
|
||||
currentFieldId=currentField.id
|
||||
fieldTypes=fieldTypes
|
||||
removeField="removeField"
|
||||
|
|
|
@ -4,14 +4,15 @@ import getUrl from "discourse-common/lib/get-url";
|
|||
export default StepController.extend({
|
||||
actions: {
|
||||
goNext(response) {
|
||||
const next = this.get("step.next");
|
||||
let nextStepId = response["next_step_id"];
|
||||
|
||||
if (response.redirect_on_next) {
|
||||
window.location.href = response.redirect_on_next;
|
||||
} else if (response.refresh_required) {
|
||||
const id = this.get("wizard.id");
|
||||
window.location.href = getUrl(`/w/${id}/steps/${next}`);
|
||||
const wizardId = this.get("wizard.id");
|
||||
window.location.href = getUrl(`/w/${wizardId}/steps/${nextStepId}`);
|
||||
} else {
|
||||
this.transitionToRoute("custom.step", next);
|
||||
this.transitionToRoute("custom.step", nextStepId);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@ export default {
|
|||
const CustomWizard = requirejs(
|
||||
"discourse/plugins/discourse-custom-wizard/wizard/models/custom"
|
||||
).default;
|
||||
const updateCachedWizard = requirejs(
|
||||
"discourse/plugins/discourse-custom-wizard/wizard/models/custom"
|
||||
).updateCachedWizard;
|
||||
const StepModel = requirejs("wizard/models/step").default;
|
||||
const StepComponent = requirejs("wizard/components/wizard-step").default;
|
||||
const ajax = requirejs("wizard/lib/ajax").ajax;
|
||||
|
@ -18,6 +21,7 @@ export default {
|
|||
"discourse/plugins/discourse-custom-wizard/wizard/lib/text-lite"
|
||||
).cook;
|
||||
const { schedule } = requirejs("@ember/runloop");
|
||||
const { alias, not } = requirejs("@ember/object/computed");
|
||||
|
||||
StepModel.reopen({
|
||||
save() {
|
||||
|
@ -155,12 +159,17 @@ export default {
|
|||
this.sendAction("showMessage", message);
|
||||
}.observes("step.message"),
|
||||
|
||||
showNextButton: not("step.final"),
|
||||
showDoneButton: alias("step.final"),
|
||||
|
||||
advance() {
|
||||
this.set("saving", true);
|
||||
this.get("step")
|
||||
.save()
|
||||
.then((response) => {
|
||||
if (this.get("finalStep")) {
|
||||
updateCachedWizard(CustomWizard.build(response["wizard"]));
|
||||
|
||||
if (response["final"]) {
|
||||
CustomWizard.finished(response);
|
||||
} else {
|
||||
this.sendAction("goNext", response);
|
||||
|
@ -178,7 +187,6 @@ export default {
|
|||
},
|
||||
|
||||
done() {
|
||||
this.set("finalStep", true);
|
||||
this.send("nextStep");
|
||||
},
|
||||
|
||||
|
|
|
@ -31,36 +31,15 @@ CustomWizard.reopenClass({
|
|||
}
|
||||
window.location.href = getUrl(url);
|
||||
},
|
||||
});
|
||||
|
||||
export function findCustomWizard(wizardId, params = {}) {
|
||||
let url = `/w/${wizardId}`;
|
||||
|
||||
let paramKeys = Object.keys(params).filter((k) => {
|
||||
if (k === "wizard_id") {
|
||||
return false;
|
||||
}
|
||||
return !!params[k];
|
||||
});
|
||||
|
||||
if (paramKeys.length) {
|
||||
url += "?";
|
||||
paramKeys.forEach((k, i) => {
|
||||
if (i > 0) {
|
||||
url += "&";
|
||||
}
|
||||
url += `${k}=${params[k]}`;
|
||||
});
|
||||
}
|
||||
|
||||
return ajax({ url, cache: false, dataType: "json" }).then((result) => {
|
||||
const wizard = result;
|
||||
if (!wizard) {
|
||||
build(wizardJson) {
|
||||
if (!wizardJson) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!wizard.completed) {
|
||||
wizard.steps = wizard.steps.map((step) => {
|
||||
if (!wizardJson.completed && wizardJson.steps) {
|
||||
wizardJson.steps = wizardJson.steps
|
||||
.map((step) => {
|
||||
const stepObj = Step.create(step);
|
||||
|
||||
stepObj.fields.sort((a, b) => {
|
||||
|
@ -81,13 +60,16 @@ export function findCustomWizard(wizardId, params = {}) {
|
|||
stepObj.fields = stepObj.fields.map((f) => WizardField.create(f));
|
||||
|
||||
return stepObj;
|
||||
})
|
||||
.sort((a, b) => {
|
||||
return parseFloat(a.index) - parseFloat(b.index);
|
||||
});
|
||||
}
|
||||
|
||||
if (wizard.categories) {
|
||||
if (wizardJson.categories) {
|
||||
let subcatMap = {};
|
||||
let categoriesById = {};
|
||||
let categories = wizard.categories.map((c) => {
|
||||
let categories = wizardJson.categories.map((c) => {
|
||||
if (c.parent_category_id) {
|
||||
subcatMap[c.parent_category_id] =
|
||||
subcatMap[c.parent_category_id] || [];
|
||||
|
@ -116,12 +98,47 @@ export function findCustomWizard(wizardId, params = {}) {
|
|||
Discourse.Site.currentProp("categoriesById", categoriesById);
|
||||
Discourse.Site.currentProp(
|
||||
"uncategorized_category_id",
|
||||
wizard.uncategorized_category_id
|
||||
wizardJson.uncategorized_category_id
|
||||
);
|
||||
}
|
||||
|
||||
return CustomWizard.create(wizard);
|
||||
return CustomWizard.create(wizardJson);
|
||||
},
|
||||
});
|
||||
|
||||
export function findCustomWizard(wizardId, params = {}) {
|
||||
let url = `/w/${wizardId}`;
|
||||
|
||||
let paramKeys = Object.keys(params).filter((k) => {
|
||||
if (k === "wizard_id") {
|
||||
return false;
|
||||
}
|
||||
return !!params[k];
|
||||
});
|
||||
|
||||
if (paramKeys.length) {
|
||||
url += "?";
|
||||
paramKeys.forEach((k, i) => {
|
||||
if (i > 0) {
|
||||
url += "&";
|
||||
}
|
||||
url += `${k}=${params[k]}`;
|
||||
});
|
||||
}
|
||||
|
||||
return ajax({ url, cache: false, dataType: "json" }).then((result) => {
|
||||
return CustomWizard.build(result);
|
||||
});
|
||||
}
|
||||
|
||||
let _wizard_store;
|
||||
|
||||
export function updateCachedWizard(wizard) {
|
||||
_wizard_store = wizard;
|
||||
}
|
||||
|
||||
export function getCachedWizard() {
|
||||
return _wizard_store;
|
||||
}
|
||||
|
||||
export default CustomWizard;
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
import { getCachedWizard } from "../models/custom";
|
||||
|
||||
export default Ember.Route.extend({
|
||||
beforeModel() {
|
||||
const appModel = this.modelFor("custom");
|
||||
if (
|
||||
appModel &&
|
||||
appModel.permitted &&
|
||||
!appModel.completed &&
|
||||
appModel.start
|
||||
) {
|
||||
this.replaceWith("custom.step", appModel.start);
|
||||
const wizard = getCachedWizard();
|
||||
if (wizard && wizard.permitted && !wizard.completed && wizard.start) {
|
||||
this.replaceWith("custom.step", wizard.start);
|
||||
}
|
||||
},
|
||||
|
||||
model() {
|
||||
return this.modelFor("custom");
|
||||
return getCachedWizard();
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
if (model) {
|
||||
if (model && model.id) {
|
||||
const completed = model.get("completed");
|
||||
const permitted = model.get("permitted");
|
||||
const wizardId = model.get("id");
|
||||
|
|
|
@ -1,28 +1,33 @@
|
|||
import WizardI18n from "../lib/wizard-i18n";
|
||||
import { getCachedWizard } from "../models/custom";
|
||||
|
||||
export default Ember.Route.extend({
|
||||
model(params) {
|
||||
const appModel = this.modelFor("custom");
|
||||
const allSteps = appModel.steps;
|
||||
if (allSteps) {
|
||||
const step = allSteps.findBy("id", params.step_id);
|
||||
return step ? step : allSteps[0];
|
||||
}
|
||||
beforeModel() {
|
||||
this.set("wizard", getCachedWizard());
|
||||
},
|
||||
|
||||
return appModel;
|
||||
model(params) {
|
||||
const wizard = this.wizard;
|
||||
|
||||
if (wizard && wizard.steps) {
|
||||
const step = wizard.steps.findBy("id", params.step_id);
|
||||
return step ? step : wizard.steps[0];
|
||||
} else {
|
||||
return wizard;
|
||||
}
|
||||
},
|
||||
|
||||
afterModel(model) {
|
||||
if (model.completed) {
|
||||
return this.transitionTo("index");
|
||||
}
|
||||
return model.set("wizardId", this.modelFor("custom").id);
|
||||
return model.set("wizardId", this.wizard.id);
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
let props = {
|
||||
step: model,
|
||||
wizard: this.modelFor("custom"),
|
||||
wizard: this.wizard,
|
||||
};
|
||||
|
||||
if (!model.permitted) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint no-undef: 0*/
|
||||
|
||||
import { findCustomWizard } from "../models/custom";
|
||||
import { findCustomWizard, updateCachedWizard } from "../models/custom";
|
||||
import { ajax } from "wizard/lib/ajax";
|
||||
|
||||
export default Ember.Route.extend({
|
||||
|
@ -12,7 +12,9 @@ export default Ember.Route.extend({
|
|||
return findCustomWizard(params.wizard_id, this.get("queryParams"));
|
||||
},
|
||||
|
||||
afterModel() {
|
||||
afterModel(model) {
|
||||
updateCachedWizard(model);
|
||||
|
||||
return ajax({
|
||||
url: `/site/settings`,
|
||||
type: "GET",
|
||||
|
@ -25,11 +27,11 @@ export default Ember.Route.extend({
|
|||
const background = model ? model.get("background") : "AliceBlue";
|
||||
Ember.run.scheduleOnce("afterRender", this, function () {
|
||||
$("body.custom-wizard").css("background", background);
|
||||
if (model) {
|
||||
$("#custom-wizard-main").addClass(model.get("id").dasherize());
|
||||
|
||||
if (model && model.id) {
|
||||
$("#custom-wizard-main").addClass(model.id.dasherize());
|
||||
}
|
||||
});
|
||||
|
||||
controller.setProperties({
|
||||
customWizard: true,
|
||||
logoUrl: Wizard.SiteSettings.logo_small,
|
||||
|
|
|
@ -303,6 +303,16 @@
|
|||
max-width: 250px !important;
|
||||
min-width: 250px !important;
|
||||
}
|
||||
|
||||
&.force-final {
|
||||
padding: 1em;
|
||||
background-color: var(--primary-very-low);
|
||||
|
||||
label,
|
||||
span {
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.full,
|
||||
|
|
|
@ -50,6 +50,11 @@ textarea {
|
|||
border-color: var(--danger);
|
||||
box-shadow: shadow("focus-danger");
|
||||
}
|
||||
|
||||
&[type="checkbox"] {
|
||||
margin-bottom: 0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.spinner {
|
||||
|
|
|
@ -56,6 +56,8 @@ en:
|
|||
undo: "Undo"
|
||||
clear: "Clear"
|
||||
select_type: "Select a type"
|
||||
condition: "Condition"
|
||||
index: "Index"
|
||||
|
||||
message:
|
||||
wizard:
|
||||
|
@ -153,6 +155,9 @@ en:
|
|||
not_permitted_message: "Message shown when required data not present"
|
||||
permitted_params:
|
||||
label: "Params"
|
||||
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"
|
||||
|
|
|
@ -41,7 +41,7 @@ class CustomWizard::AdminManagerController < CustomWizard::AdminController
|
|||
end
|
||||
|
||||
begin
|
||||
template_json = JSON.parse file
|
||||
template_json = JSON.parse(file)
|
||||
rescue JSON::ParserError
|
||||
return render_error(I18n.t('wizard.import.error.invalid_json'))
|
||||
end
|
||||
|
|
|
@ -37,7 +37,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
|
|||
wizard_id = template.save(create: params[:create])
|
||||
|
||||
if template.errors.any?
|
||||
render json: failed_json.merge(errors: result.errors.full_messages)
|
||||
render json: failed_json.merge(errors: template.errors.full_messages)
|
||||
else
|
||||
render json: success_json.merge(wizard_id: wizard_id)
|
||||
end
|
||||
|
@ -83,15 +83,19 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
|
|||
permitted: mapped_params,
|
||||
steps: [
|
||||
:id,
|
||||
:index,
|
||||
:title,
|
||||
:key,
|
||||
:banner,
|
||||
:raw_description,
|
||||
:required_data_message,
|
||||
:force_final,
|
||||
required_data: mapped_params,
|
||||
permitted_params: mapped_params,
|
||||
condition: mapped_params,
|
||||
fields: [
|
||||
:id,
|
||||
:index,
|
||||
:label,
|
||||
:image,
|
||||
:description,
|
||||
|
@ -107,6 +111,8 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
|
|||
:property,
|
||||
prefill: mapped_params,
|
||||
content: mapped_params,
|
||||
condition: mapped_params,
|
||||
index: mapped_params,
|
||||
validations: {},
|
||||
]
|
||||
],
|
||||
|
|
|
@ -4,31 +4,67 @@ class CustomWizard::StepsController < ::ApplicationController
|
|||
before_action :ensure_can_update
|
||||
|
||||
def update
|
||||
params.require(:step_id)
|
||||
params.require(:wizard_id)
|
||||
|
||||
wizard = @builder.build
|
||||
step = wizard.steps.select { |s| s.id == update_params[:step_id] }.first
|
||||
|
||||
raise Discourse::InvalidParameters.new(:step_id) if !step
|
||||
|
||||
update = update_params.to_h
|
||||
|
||||
update[:fields] = {}
|
||||
if params[:fields]
|
||||
field_ids = step.fields.map(&:id)
|
||||
field_ids = @step_template['fields'].map { |f| f['id'] }
|
||||
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])
|
||||
@builder.build
|
||||
|
||||
updater = @builder.wizard.create_updater(update[:step_id], update[:fields])
|
||||
updater.update
|
||||
@result = updater.result
|
||||
|
||||
if updater.success?
|
||||
result = success_json
|
||||
result.merge!(updater.result) if updater.result
|
||||
wizard_id = update_params[:wizard_id]
|
||||
builder = CustomWizard::Builder.new(wizard_id, current_user)
|
||||
@wizard = builder.build
|
||||
|
||||
current_step = @wizard.find_step(update[:step_id])
|
||||
current_submission = @wizard.current_submission
|
||||
result = {}
|
||||
|
||||
if current_step.conditional_final_step && !current_step.last_step
|
||||
current_step.force_final = true
|
||||
end
|
||||
|
||||
if current_step.final?
|
||||
builder.template.actions.each do |action_template|
|
||||
if action_template['run_after'] === 'wizard_completion'
|
||||
CustomWizard::Action.new(
|
||||
action: action_template,
|
||||
wizard: @wizard,
|
||||
data: current_submission
|
||||
).perform
|
||||
end
|
||||
end
|
||||
|
||||
@wizard.save_submission(current_submission)
|
||||
|
||||
if redirect = get_redirect
|
||||
updater.result[:redirect_on_complete] = redirect
|
||||
end
|
||||
|
||||
@wizard.final_cleanup!
|
||||
|
||||
result[:final] = true
|
||||
else
|
||||
result[:final] = false
|
||||
result[:next_step_id] = current_step.next.id
|
||||
end
|
||||
|
||||
result.merge!(updater.result) if updater.result.present?
|
||||
result[:refresh_required] = true if updater.refresh_required?
|
||||
result[:wizard] = ::CustomWizard::WizardSerializer.new(
|
||||
@wizard,
|
||||
scope: Guardian.new(current_user),
|
||||
root: false
|
||||
).as_json
|
||||
|
||||
render json: result
|
||||
else
|
||||
|
@ -43,21 +79,31 @@ class CustomWizard::StepsController < ::ApplicationController
|
|||
private
|
||||
|
||||
def ensure_can_update
|
||||
@builder = CustomWizard::Builder.new(
|
||||
update_params[:wizard_id].underscore,
|
||||
current_user
|
||||
)
|
||||
@builder = CustomWizard::Builder.new(update_params[:wizard_id], current_user)
|
||||
raise Discourse::InvalidParameters.new(:wizard_id) if @builder.template.nil?
|
||||
raise Discourse::InvalidAccess.new if !@builder.wizard || !@builder.wizard.can_access?
|
||||
|
||||
if @builder.nil?
|
||||
raise Discourse::InvalidParameters.new(:wizard_id)
|
||||
end
|
||||
|
||||
if !@builder.wizard || !@builder.wizard.can_access?
|
||||
raise Discourse::InvalidAccess.new
|
||||
end
|
||||
@step_template = @builder.template.steps.select do |s|
|
||||
s['id'] == update_params[:step_id]
|
||||
end.first
|
||||
raise Discourse::InvalidParameters.new(:step_id) if !@step_template
|
||||
raise Discourse::InvalidAccess.new if !@builder.check_condition(@step_template)
|
||||
end
|
||||
|
||||
def update_params
|
||||
params.permit(:wizard_id, :step_id)
|
||||
@update_params || begin
|
||||
params.require(:step_id)
|
||||
params.require(:wizard_id)
|
||||
params.permit(:wizard_id, :step_id).transform_values { |v| v.underscore }
|
||||
end
|
||||
end
|
||||
|
||||
def get_redirect
|
||||
return @result[:redirect_on_next] if @result[:redirect_on_next].present?
|
||||
|
||||
current_submission = @wizard.current_submission
|
||||
return nil unless current_submission.present?
|
||||
## route_to set by actions, redirect_on_complete set by actions, redirect_to set at wizard entry
|
||||
current_submission[:route_to] || current_submission[:redirect_on_complete] || current_submission[:redirect_to]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -65,10 +65,7 @@ class CustomWizard::WizardController < ::ApplicationController
|
|||
result.merge!(redirect_to: submission['redirect_to'])
|
||||
end
|
||||
|
||||
if user.custom_fields['redirect_to_wizard'] === wizard.id
|
||||
user.custom_fields.delete('redirect_to_wizard')
|
||||
user.save_custom_fields(true)
|
||||
end
|
||||
wizard.final_cleanup!
|
||||
end
|
||||
|
||||
render json: result
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"result": {
|
||||
"line": 89.56
|
||||
"line": 90.52
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
module CustomWizardFieldExtension
|
||||
attr_reader :raw,
|
||||
:label,
|
||||
:description,
|
||||
:image,
|
||||
:key,
|
||||
:validations,
|
||||
:min_length,
|
||||
:max_length,
|
||||
:char_counter,
|
||||
:file_types,
|
||||
:format,
|
||||
:limit,
|
||||
:property,
|
||||
:content,
|
||||
:number
|
||||
|
||||
def initialize(attrs)
|
||||
super
|
||||
@raw = attrs || {}
|
||||
@description = attrs[:description]
|
||||
@image = attrs[:image]
|
||||
@key = attrs[:key]
|
||||
@validations = attrs[:validations]
|
||||
@min_length = attrs[:min_length]
|
||||
@max_length = attrs[:max_length]
|
||||
@char_counter = attrs[:char_counter]
|
||||
@file_types = attrs[:file_types]
|
||||
@format = attrs[:format]
|
||||
@limit = attrs[:limit]
|
||||
@property = attrs[:property]
|
||||
@content = attrs[:content]
|
||||
@number = attrs[:number]
|
||||
end
|
||||
|
||||
def label
|
||||
@label ||= PrettyText.cook(@raw[:label])
|
||||
end
|
||||
end
|
|
@ -1,4 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
module CustomWizardStepExtension
|
||||
attr_accessor :title, :description, :key, :permitted, :permitted_message
|
||||
end
|
|
@ -6,12 +6,12 @@ class CustomWizard::Action
|
|||
:guardian,
|
||||
:result
|
||||
|
||||
def initialize(params)
|
||||
@wizard = params[:wizard]
|
||||
@action = params[:action]
|
||||
@user = params[:user]
|
||||
def initialize(opts)
|
||||
@wizard = opts[:wizard]
|
||||
@action = opts[:action]
|
||||
@user = @wizard.user
|
||||
@guardian = Guardian.new(@user)
|
||||
@data = params[:data]
|
||||
@data = opts[:data]
|
||||
@log = []
|
||||
@result = CustomWizard::ActionResult.new
|
||||
end
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
class CustomWizard::Builder
|
||||
attr_accessor :wizard, :updater, :submissions
|
||||
attr_accessor :wizard, :updater, :template
|
||||
|
||||
def initialize(wizard_id, user = nil)
|
||||
template = CustomWizard::Template.find(wizard_id)
|
||||
return nil if template.blank?
|
||||
|
||||
@wizard = CustomWizard::Wizard.new(template, user)
|
||||
@steps = template['steps'] || []
|
||||
@actions = template['actions'] || []
|
||||
@submissions = @wizard.submissions
|
||||
@template = CustomWizard::Template.create(wizard_id)
|
||||
return nil if @template.nil?
|
||||
@wizard = CustomWizard::Wizard.new(template.data, user)
|
||||
end
|
||||
|
||||
def self.sorted_handlers
|
||||
|
@ -28,7 +24,7 @@ class CustomWizard::Builder
|
|||
def mapper
|
||||
CustomWizard::Mapper.new(
|
||||
user: @wizard.user,
|
||||
data: @submissions.last
|
||||
data: @wizard.current_submission
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -38,103 +34,38 @@ class CustomWizard::Builder
|
|||
|
||||
build_opts[:reset] = build_opts[:reset] || @wizard.restart_on_revisit
|
||||
|
||||
@steps.each do |step_template|
|
||||
@template.steps.each do |step_template|
|
||||
next if !check_condition(step_template)
|
||||
|
||||
@wizard.append_step(step_template['id']) do |step|
|
||||
step.permitted = true
|
||||
step = check_if_permitted(step, step_template)
|
||||
next if !step.permitted
|
||||
|
||||
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.banner = step_template['banner'] if step_template['banner']
|
||||
step.key = step_template['key'] if step_template['key']
|
||||
|
||||
if step_template['description']
|
||||
step.description = mapper.interpolate(
|
||||
step_template['description'],
|
||||
user: true,
|
||||
value: true
|
||||
)
|
||||
end
|
||||
|
||||
if permitted_params = step_template['permitted_params']
|
||||
save_permitted_params(permitted_params, params)
|
||||
end
|
||||
|
||||
if step_template['fields'] && step_template['fields'].length
|
||||
step_template['fields'].each_with_index do |field_template, index|
|
||||
append_field(step, step_template, field_template, build_opts, index)
|
||||
end
|
||||
end
|
||||
save_permitted_params(step_template, params)
|
||||
step = add_step_attributes(step, step_template)
|
||||
step = append_step_fields(step, step_template, build_opts)
|
||||
|
||||
step.on_update do |updater|
|
||||
@updater = updater
|
||||
user = @wizard.user
|
||||
@submission = (@wizard.current_submission || {})
|
||||
.merge(@updater.submission)
|
||||
.with_indifferent_access
|
||||
|
||||
updater.validate
|
||||
@updater.validate
|
||||
next if @updater.errors.any?
|
||||
|
||||
next if updater.errors.any?
|
||||
apply_step_handlers
|
||||
next if @updater.errors.any?
|
||||
|
||||
CustomWizard::Builder.step_handlers.each do |handler|
|
||||
if handler[:wizard_id] == @wizard.id
|
||||
handler[:block].call(self)
|
||||
end
|
||||
run_step_actions
|
||||
|
||||
if @updater.errors.empty?
|
||||
if route_to = @submission['route_to']
|
||||
@submission.delete('route_to')
|
||||
end
|
||||
|
||||
next if updater.errors.any?
|
||||
|
||||
submission = updater.submission
|
||||
|
||||
if current_submission = @wizard.current_submission
|
||||
submission = current_submission.merge(submission)
|
||||
end
|
||||
|
||||
final_step = updater.step.next.nil?
|
||||
|
||||
if @actions.present?
|
||||
@actions.each do |action|
|
||||
|
||||
if (action['run_after'] === updater.step.id) ||
|
||||
(final_step && (!action['run_after'] || (action['run_after'] === 'wizard_completion')))
|
||||
|
||||
CustomWizard::Action.new(
|
||||
wizard: @wizard,
|
||||
action: action,
|
||||
user: user,
|
||||
data: submission
|
||||
).perform
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if updater.errors.empty?
|
||||
if route_to = submission['route_to']
|
||||
submission.delete('route_to')
|
||||
end
|
||||
|
||||
if @wizard.save_submissions
|
||||
save_submissions(submission, final_step)
|
||||
end
|
||||
|
||||
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 || submission['redirect_on_complete'] || submission["redirect_to"]
|
||||
updater.result[:redirect_on_complete] = redirect_url
|
||||
elsif route_to
|
||||
updater.result[:redirect_on_next] = route_to
|
||||
end
|
||||
@wizard.save_submission(@submission)
|
||||
@updater.result[:redirect_on_next] = route_to if route_to
|
||||
|
||||
true
|
||||
else
|
||||
|
@ -144,25 +75,21 @@ class CustomWizard::Builder
|
|||
end
|
||||
end
|
||||
|
||||
@wizard.update_step_order!
|
||||
@wizard
|
||||
end
|
||||
|
||||
def append_field(step, step_template, field_template, build_opts, index)
|
||||
def append_field(step, step_template, field_template, build_opts)
|
||||
params = {
|
||||
id: field_template['id'],
|
||||
type: field_template['type'],
|
||||
required: field_template['required'],
|
||||
number: index + 1
|
||||
required: field_template['required']
|
||||
}
|
||||
|
||||
params[:label] = field_template['label'] if field_template['label']
|
||||
params[:description] = field_template['description'] if field_template['description']
|
||||
params[:image] = field_template['image'] if field_template['image']
|
||||
params[:key] = field_template['key'] if field_template['key']
|
||||
params[:validations] = field_template['validations'] if field_template['validations']
|
||||
params[:min_length] = field_template['min_length'] if field_template['min_length']
|
||||
params[:max_length] = field_template['max_length'] if field_template['max_length']
|
||||
params[:char_counter] = field_template['char_counter'] if field_template['char_counter']
|
||||
%w(label description image key validations min_length max_length char_counter).each do |key|
|
||||
params[key.to_sym] = field_template[key] if field_template[key]
|
||||
end
|
||||
|
||||
params[:value] = prefill_field(field_template, step_template)
|
||||
|
||||
if !build_opts[:reset] && (submission = @wizard.current_submission)
|
||||
|
@ -209,7 +136,7 @@ class CustomWizard::Builder
|
|||
content = CustomWizard::Mapper.new(
|
||||
inputs: content_inputs,
|
||||
user: @wizard.user,
|
||||
data: @submissions.last,
|
||||
data: @wizard.current_submission,
|
||||
opts: {
|
||||
with_type: true
|
||||
}
|
||||
|
@ -240,6 +167,16 @@ class CustomWizard::Builder
|
|||
end
|
||||
end
|
||||
|
||||
if field_template['index'].present?
|
||||
index = CustomWizard::Mapper.new(
|
||||
inputs: field_template['index'],
|
||||
user: @wizard.user,
|
||||
data: @wizard.current_submission
|
||||
).perform
|
||||
|
||||
params[:index] = index.to_i unless index.nil?
|
||||
end
|
||||
|
||||
field = step.add_field(params)
|
||||
end
|
||||
|
||||
|
@ -248,41 +185,91 @@ class CustomWizard::Builder
|
|||
CustomWizard::Mapper.new(
|
||||
inputs: prefill,
|
||||
user: @wizard.user,
|
||||
data: @submissions.last
|
||||
data: @wizard.current_submission
|
||||
).perform
|
||||
end
|
||||
end
|
||||
|
||||
def check_condition(template)
|
||||
if template['condition'].present?
|
||||
CustomWizard::Mapper.new(
|
||||
inputs: template['condition'],
|
||||
user: @wizard.user,
|
||||
data: @wizard.current_submission
|
||||
).perform
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def check_if_permitted(step, step_template)
|
||||
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
|
||||
end
|
||||
|
||||
step
|
||||
end
|
||||
|
||||
def add_step_attributes(step, step_template)
|
||||
%w(index title banner key force_final).each do |attr|
|
||||
step.send("#{attr}=", step_template[attr]) if step_template[attr]
|
||||
end
|
||||
|
||||
if step_template['description']
|
||||
step.description = mapper.interpolate(
|
||||
step_template['description'],
|
||||
user: true,
|
||||
value: true
|
||||
)
|
||||
end
|
||||
|
||||
step
|
||||
end
|
||||
|
||||
def append_step_fields(step, step_template, build_opts)
|
||||
if step_template['fields'] && step_template['fields'].length
|
||||
step_template['fields'].each do |field_template|
|
||||
next if !check_condition(field_template)
|
||||
append_field(step, step_template, field_template, build_opts)
|
||||
end
|
||||
end
|
||||
|
||||
step.update_field_order!
|
||||
step
|
||||
end
|
||||
|
||||
def standardise_boolean(value)
|
||||
ActiveRecord::Type::Boolean.new.cast(value)
|
||||
end
|
||||
|
||||
def save_submissions(submission, final_step)
|
||||
if final_step
|
||||
submission['submitted_at'] = Time.now.iso8601
|
||||
end
|
||||
def save_permitted_params(step_template, params)
|
||||
return unless step_template['permitted_params'].present?
|
||||
|
||||
if submission.present?
|
||||
@submissions.pop(1) if @wizard.unfinished?
|
||||
@submissions.push(submission)
|
||||
@wizard.set_submissions(@submissions)
|
||||
end
|
||||
end
|
||||
|
||||
def save_permitted_params(permitted_params, params)
|
||||
permitted_params = step_template['permitted_params']
|
||||
permitted_data = {}
|
||||
submission_key = nil
|
||||
params_key = nil
|
||||
submission = @wizard.current_submission || {}
|
||||
|
||||
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]
|
||||
|
||||
if submission_key && params_key
|
||||
submission[submission_key] = params[params_key]
|
||||
end
|
||||
end
|
||||
|
||||
if permitted_data.present?
|
||||
current_data = @submissions.last || {}
|
||||
save_submissions(current_data.merge(permitted_data), false)
|
||||
end
|
||||
@wizard.save_submission(submission)
|
||||
end
|
||||
|
||||
def ensure_required_data(step, step_template)
|
||||
|
@ -291,13 +278,13 @@ class CustomWizard::Builder
|
|||
pair['key'].present? && pair['value'].present?
|
||||
end
|
||||
|
||||
if pairs.any? && !@submissions.last
|
||||
if pairs.any? && !@wizard.current_submission
|
||||
step.permitted = false
|
||||
break
|
||||
end
|
||||
|
||||
pairs.each do |pair|
|
||||
pair['key'] = @submissions.last[pair['key']]
|
||||
pair['key'] = @wizard.current_submission[pair['key']]
|
||||
end
|
||||
|
||||
if !mapper.validate_pairs(pairs)
|
||||
|
@ -308,4 +295,26 @@ class CustomWizard::Builder
|
|||
|
||||
step
|
||||
end
|
||||
|
||||
def apply_step_handlers
|
||||
CustomWizard::Builder.step_handlers.each do |handler|
|
||||
if handler[:wizard_id] == @wizard.id
|
||||
handler[:block].call(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def run_step_actions
|
||||
if @template.actions.present?
|
||||
@template.actions.each do |action_template|
|
||||
if action_template['run_after'] === updater.step.id
|
||||
CustomWizard::Action.new(
|
||||
action: action_template,
|
||||
wizard: @wizard,
|
||||
data: @submission
|
||||
).perform
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,55 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CustomWizard::Field
|
||||
include ActiveModel::SerializerSupport
|
||||
|
||||
attr_reader :raw,
|
||||
:id,
|
||||
:type,
|
||||
:required,
|
||||
:value,
|
||||
:label,
|
||||
:description,
|
||||
:image,
|
||||
:key,
|
||||
:validations,
|
||||
:min_length,
|
||||
:max_length,
|
||||
:char_counter,
|
||||
:file_types,
|
||||
:format,
|
||||
:limit,
|
||||
:property,
|
||||
:content
|
||||
|
||||
attr_accessor :index,
|
||||
:step
|
||||
|
||||
def initialize(attrs)
|
||||
@raw = attrs || {}
|
||||
@id = attrs[:id]
|
||||
@index = attrs[:index]
|
||||
@type = attrs[:type]
|
||||
@required = !!attrs[:required]
|
||||
@value = attrs[:value]
|
||||
@description = attrs[:description]
|
||||
@image = attrs[:image]
|
||||
@key = attrs[:key]
|
||||
@validations = attrs[:validations]
|
||||
@min_length = attrs[:min_length]
|
||||
@max_length = attrs[:max_length]
|
||||
@char_counter = attrs[:char_counter]
|
||||
@file_types = attrs[:file_types]
|
||||
@format = attrs[:format]
|
||||
@limit = attrs[:limit]
|
||||
@property = attrs[:property]
|
||||
@content = attrs[:content]
|
||||
end
|
||||
|
||||
def label
|
||||
@label ||= PrettyText.cook(@raw[:label])
|
||||
end
|
||||
|
||||
def self.types
|
||||
@types ||= {
|
||||
text: {
|
||||
|
|
56
lib/custom_wizard/step.rb
Normale Datei
56
lib/custom_wizard/step.rb
Normale Datei
|
@ -0,0 +1,56 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CustomWizard::Step
|
||||
include ActiveModel::SerializerSupport
|
||||
|
||||
attr_reader :id,
|
||||
:updater
|
||||
|
||||
attr_accessor :index,
|
||||
:title,
|
||||
:description,
|
||||
:key,
|
||||
:permitted,
|
||||
:permitted_message,
|
||||
:fields,
|
||||
:next,
|
||||
:previous,
|
||||
:banner,
|
||||
:disabled,
|
||||
:description_vars,
|
||||
:last_step,
|
||||
:force_final,
|
||||
:conditional_final_step,
|
||||
:wizard
|
||||
|
||||
def initialize(id)
|
||||
@id = id
|
||||
@fields = []
|
||||
end
|
||||
|
||||
def add_field(attrs)
|
||||
field = ::CustomWizard::Field.new(attrs)
|
||||
field.index = (@fields.size == 1 ? 0 : @fields.size) if field.index.nil?
|
||||
field.step = self
|
||||
@fields << field
|
||||
field
|
||||
end
|
||||
|
||||
def has_fields?
|
||||
@fields.present?
|
||||
end
|
||||
|
||||
def on_update(&block)
|
||||
@updater = block
|
||||
end
|
||||
|
||||
def update_field_order!
|
||||
@fields.sort_by!(&:index)
|
||||
end
|
||||
|
||||
def final?
|
||||
return true if force_final && conditional_final_step
|
||||
return true if last_step
|
||||
false
|
||||
end
|
||||
end
|
|
@ -2,14 +2,15 @@
|
|||
class CustomWizard::StepUpdater
|
||||
include ActiveModel::Model
|
||||
|
||||
attr_accessor :refresh_required, :submission, :result, :step
|
||||
attr_accessor :refresh_required, :result
|
||||
attr_reader :step, :submission
|
||||
|
||||
def initialize(current_user, wizard, step, submission)
|
||||
@current_user = current_user
|
||||
@wizard = wizard
|
||||
@step = step
|
||||
@refresh_required = false
|
||||
@submission = submission.to_h.with_indifferent_access
|
||||
@submission = submission.with_indifferent_access
|
||||
@result = {}
|
||||
end
|
||||
|
||||
|
|
|
@ -4,10 +4,14 @@ class CustomWizard::Template
|
|||
include HasErrors
|
||||
|
||||
attr_reader :data,
|
||||
:opts
|
||||
:opts,
|
||||
:steps,
|
||||
:actions
|
||||
|
||||
def initialize(data)
|
||||
@data = data
|
||||
@steps = data['steps'] || []
|
||||
@actions = data['actions'] || []
|
||||
end
|
||||
|
||||
def save(opts = {})
|
||||
|
@ -31,6 +35,14 @@ class CustomWizard::Template
|
|||
new(data).save(opts)
|
||||
end
|
||||
|
||||
def self.create(wizard_id)
|
||||
if data = find(wizard_id)
|
||||
new(data)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def self.find(wizard_id)
|
||||
PluginStore.get(CustomWizard::PLUGIN_NAME, wizard_id)
|
||||
end
|
||||
|
@ -88,6 +100,12 @@ class CustomWizard::Template
|
|||
if step[:raw_description]
|
||||
step[:description] = PrettyText.cook(step[:raw_description])
|
||||
end
|
||||
|
||||
remove_non_mapped_index(step)
|
||||
|
||||
step[:fields].each do |field|
|
||||
remove_non_mapped_index(field)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -118,4 +136,10 @@ class CustomWizard::Template
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
def remove_non_mapped_index(object)
|
||||
if !object[:index].is_a?(Array)
|
||||
object.delete(:index)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,9 +26,10 @@ class CustomWizard::Wizard
|
|||
:needs_groups,
|
||||
:steps,
|
||||
:step_ids,
|
||||
:first_step,
|
||||
:start,
|
||||
:actions,
|
||||
:user,
|
||||
:first_step
|
||||
:user
|
||||
|
||||
def initialize(attrs = {}, user = nil)
|
||||
@user = user
|
||||
|
@ -68,8 +69,8 @@ class CustomWizard::Wizard
|
|||
val.nil? ? false : ActiveRecord::Type::Boolean.new.cast(val)
|
||||
end
|
||||
|
||||
def create_step(step_name)
|
||||
::Wizard::Step.new(step_name)
|
||||
def create_step(step_id)
|
||||
::CustomWizard::Step.new(step_id)
|
||||
end
|
||||
|
||||
def append_step(step)
|
||||
|
@ -77,37 +78,58 @@ class CustomWizard::Wizard
|
|||
|
||||
yield step if block_given?
|
||||
|
||||
last_step = steps.last
|
||||
steps << step
|
||||
step.wizard = self
|
||||
step.index = (steps.size == 1 ? 0 : steps.size) if step.index.nil?
|
||||
end
|
||||
|
||||
if steps.size == 1
|
||||
def update_step_order!
|
||||
steps.sort_by!(&:index)
|
||||
|
||||
steps.each_with_index do |step, index|
|
||||
if index === 0
|
||||
@first_step = step
|
||||
step.index = 0
|
||||
elsif last_step.present?
|
||||
@start = step.id
|
||||
else
|
||||
last_step = steps[index - 1]
|
||||
last_step.next = step
|
||||
step.previous = last_step
|
||||
step.index = last_step.index + 1
|
||||
end
|
||||
|
||||
step.index = index
|
||||
|
||||
if index === (steps.length - 1)
|
||||
step.conditional_final_step = true
|
||||
end
|
||||
|
||||
if index === (step_ids.length - 1)
|
||||
step.last_step = true
|
||||
end
|
||||
|
||||
if step.previous && step.previous.id === last_completed_step_id
|
||||
@start = step.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def start
|
||||
return nil if !user
|
||||
|
||||
if unfinished? && last_completed_step = ::UserHistory.where(
|
||||
def last_completed_step_id
|
||||
if user && unfinished? && last_completed_step = ::UserHistory.where(
|
||||
acting_user_id: user.id,
|
||||
action: ::UserHistory.actions[:custom_wizard_step],
|
||||
context: id,
|
||||
subject: steps.map(&:id)
|
||||
subject: step_ids
|
||||
).order("created_at").last
|
||||
|
||||
step_id = last_completed_step.subject
|
||||
last_index = steps.index { |s| s.id == step_id }
|
||||
steps[last_index + 1]
|
||||
last_completed_step.subject
|
||||
else
|
||||
@first_step
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def find_step(step_id)
|
||||
steps.select { |step| step.id === step_id }.first
|
||||
end
|
||||
|
||||
def create_updater(step_id, submission)
|
||||
step = @steps.find { |s| s.id == step_id }
|
||||
wizard = self
|
||||
|
@ -200,12 +222,13 @@ class CustomWizard::Wizard
|
|||
end
|
||||
|
||||
def submissions
|
||||
Array.wrap(PluginStore.get("#{id}_submissions", user.id))
|
||||
return nil unless user.present?
|
||||
@submissions ||= Array.wrap(PluginStore.get("#{id}_submissions", user.id))
|
||||
end
|
||||
|
||||
def current_submission
|
||||
if submissions.present? && !submissions.last.key?("submitted_at")
|
||||
submissions.last
|
||||
if submissions.present? && submissions.last.present? && !submissions.last.key?("submitted_at")
|
||||
submissions.last.with_indifferent_access
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
@ -213,6 +236,27 @@ class CustomWizard::Wizard
|
|||
|
||||
def set_submissions(submissions)
|
||||
PluginStore.set("#{id}_submissions", user.id, Array.wrap(submissions))
|
||||
@submissions = nil
|
||||
end
|
||||
|
||||
def save_submission(submission)
|
||||
return nil unless save_submissions
|
||||
|
||||
submissions.pop(1) if unfinished?
|
||||
submissions.push(submission)
|
||||
set_submissions(submissions)
|
||||
end
|
||||
|
||||
def final_cleanup!
|
||||
if id == user.custom_fields['redirect_to_wizard']
|
||||
user.custom_fields.delete('redirect_to_wizard')
|
||||
user.save_custom_fields(true)
|
||||
end
|
||||
|
||||
if submission = current_submission
|
||||
submission['submitted_at'] = Time.now.iso8601
|
||||
save_submission(submission)
|
||||
end
|
||||
end
|
||||
|
||||
def self.submissions(wizard_id, user)
|
||||
|
@ -276,7 +320,7 @@ class CustomWizard::Wizard
|
|||
end
|
||||
|
||||
def self.set_submission_redirect(user, wizard_id, url)
|
||||
PluginStore.set("#{wizard_id.underscore}_submissions", user.id, [{ redirect_to: url }])
|
||||
set_submissions(wizard_id, user, [{ redirect_to: url }])
|
||||
end
|
||||
|
||||
def self.set_wizard_redirect(wizard_id, user)
|
||||
|
|
|
@ -66,6 +66,7 @@ after_initialize do
|
|||
../lib/custom_wizard/mapper.rb
|
||||
../lib/custom_wizard/log.rb
|
||||
../lib/custom_wizard/step_updater.rb
|
||||
../lib/custom_wizard/step.rb
|
||||
../lib/custom_wizard/template.rb
|
||||
../lib/custom_wizard/wizard.rb
|
||||
../lib/custom_wizard/api/api.rb
|
||||
|
@ -89,8 +90,6 @@ after_initialize do
|
|||
../extensions/extra_locales_controller.rb
|
||||
../extensions/invites_controller.rb
|
||||
../extensions/users_controller.rb
|
||||
../extensions/wizard_field.rb
|
||||
../extensions/wizard_step.rb
|
||||
../extensions/custom_field/preloader.rb
|
||||
../extensions/custom_field/serializer.rb
|
||||
].each do |path|
|
||||
|
@ -172,8 +171,6 @@ after_initialize do
|
|||
::ExtraLocalesController.prepend ExtraLocalesControllerCustomWizard
|
||||
::InvitesController.prepend InvitesControllerCustomWizard
|
||||
::UsersController.prepend CustomWizardUsersController
|
||||
::Wizard::Field.prepend CustomWizardFieldExtension
|
||||
::Wizard::Step.prepend CustomWizardStepExtension
|
||||
|
||||
full_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets/stylesheets/wizard/wizard_custom.scss"
|
||||
if Stylesheet::Importer.respond_to?(:plugin_assets)
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CustomWizard::FieldSerializer < ::WizardFieldSerializer
|
||||
class CustomWizard::FieldSerializer < ::ApplicationSerializer
|
||||
|
||||
attributes :image,
|
||||
attributes :id,
|
||||
:index,
|
||||
:type,
|
||||
:required,
|
||||
:value,
|
||||
:label,
|
||||
:placeholder,
|
||||
:description,
|
||||
:image,
|
||||
:file_types,
|
||||
:format,
|
||||
:limit,
|
||||
|
@ -10,19 +18,54 @@ class CustomWizard::FieldSerializer < ::WizardFieldSerializer
|
|||
:content,
|
||||
:validations,
|
||||
:max_length,
|
||||
:char_counter,
|
||||
:number
|
||||
:char_counter
|
||||
|
||||
def id
|
||||
object.id
|
||||
end
|
||||
|
||||
def index
|
||||
object.index
|
||||
end
|
||||
|
||||
def type
|
||||
object.type
|
||||
end
|
||||
|
||||
def required
|
||||
object.required
|
||||
end
|
||||
|
||||
def value
|
||||
object.value
|
||||
end
|
||||
|
||||
def include_value?
|
||||
object.value.present?
|
||||
end
|
||||
|
||||
def i18n_key
|
||||
@i18n_key ||= "wizard.step.#{object.step.id}.fields.#{object.id}".underscore
|
||||
end
|
||||
|
||||
def label
|
||||
return object.label if object.label.present?
|
||||
I18n.t("#{object.key || i18n_key}.label", default: '')
|
||||
end
|
||||
|
||||
def include_label?
|
||||
label.present?
|
||||
end
|
||||
|
||||
def description
|
||||
return object.description if object.description.present?
|
||||
I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url)
|
||||
end
|
||||
|
||||
def include_description?
|
||||
description.present?
|
||||
end
|
||||
|
||||
def image
|
||||
object.image
|
||||
end
|
||||
|
@ -35,6 +78,10 @@ class CustomWizard::FieldSerializer < ::WizardFieldSerializer
|
|||
I18n.t("#{object.key || i18n_key}.placeholder", default: '')
|
||||
end
|
||||
|
||||
def include_placeholder?
|
||||
placeholder.present?
|
||||
end
|
||||
|
||||
def file_types
|
||||
object.file_types
|
||||
end
|
||||
|
@ -55,10 +102,6 @@ class CustomWizard::FieldSerializer < ::WizardFieldSerializer
|
|||
object.content
|
||||
end
|
||||
|
||||
def include_choices?
|
||||
object.choices.present?
|
||||
end
|
||||
|
||||
def validations
|
||||
validations = {}
|
||||
object.validations&.each do |type, props|
|
||||
|
@ -77,8 +120,4 @@ class CustomWizard::FieldSerializer < ::WizardFieldSerializer
|
|||
def char_counter
|
||||
object.char_counter
|
||||
end
|
||||
|
||||
def number
|
||||
object.number
|
||||
end
|
||||
end
|
||||
|
|
|
@ -30,7 +30,7 @@ class CustomWizard::WizardSerializer < CustomWizard::BasicWizardSerializer
|
|||
end
|
||||
|
||||
def start
|
||||
object.start.id
|
||||
object.start
|
||||
end
|
||||
|
||||
def include_start?
|
||||
|
|
|
@ -1,20 +1,74 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CustomWizard::StepSerializer < ::WizardStepSerializer
|
||||
class CustomWizard::StepSerializer < ::ApplicationSerializer
|
||||
|
||||
attributes :id,
|
||||
:index,
|
||||
:next,
|
||||
:previous,
|
||||
:description,
|
||||
:title,
|
||||
:banner,
|
||||
:permitted,
|
||||
:permitted_message,
|
||||
:final
|
||||
|
||||
attributes :permitted, :permitted_message
|
||||
has_many :fields, serializer: ::CustomWizard::FieldSerializer, embed: :objects
|
||||
|
||||
def id
|
||||
object.id
|
||||
end
|
||||
|
||||
def index
|
||||
object.index
|
||||
end
|
||||
|
||||
def next
|
||||
object.next.id if object.next.present?
|
||||
end
|
||||
|
||||
def include_next?
|
||||
object.next.present?
|
||||
end
|
||||
|
||||
def previous
|
||||
object.previous.id if object.previous.present?
|
||||
end
|
||||
|
||||
def include_previous?
|
||||
object.previous.present?
|
||||
end
|
||||
|
||||
def i18n_key
|
||||
@i18n_key ||= "wizard.step.#{object.id}".underscore
|
||||
end
|
||||
|
||||
def title
|
||||
return PrettyText.cook(object.title) if object.title
|
||||
PrettyText.cook(I18n.t("#{object.key || i18n_key}.title", default: ''))
|
||||
end
|
||||
|
||||
def include_title?
|
||||
title.present?
|
||||
end
|
||||
|
||||
def description
|
||||
return object.description if object.description
|
||||
PrettyText.cook(I18n.t("#{object.key || i18n_key}.description", default: '', base_url: Discourse.base_url))
|
||||
end
|
||||
|
||||
def include_description?
|
||||
description.present?
|
||||
end
|
||||
|
||||
def banner
|
||||
object.banner
|
||||
end
|
||||
|
||||
def include_banner?
|
||||
object.banner.present?
|
||||
end
|
||||
|
||||
def permitted
|
||||
object.permitted
|
||||
end
|
||||
|
@ -22,4 +76,8 @@ class CustomWizard::StepSerializer < ::WizardStepSerializer
|
|||
def permitted_message
|
||||
object.permitted_message
|
||||
end
|
||||
|
||||
def final
|
||||
object.final?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,19 +6,25 @@ describe CustomWizard::Action do
|
|||
fab!(:category) { Fabricate(:category, name: 'cat1', slug: 'cat-slug') }
|
||||
fab!(:group) { Fabricate(:group) }
|
||||
|
||||
let(:wizard_template) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
let(:open_composer) {
|
||||
JSON.parse(File.open(
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/actions/open_composer.json"
|
||||
).read)
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
before do
|
||||
Group.refresh_automatic_group!(:trust_level_2)
|
||||
CustomWizard::Template.save(
|
||||
JSON.parse(File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read),
|
||||
skip_jobs: true)
|
||||
CustomWizard::Template.save(wizard_template, skip_jobs: true)
|
||||
@template = CustomWizard::Template.find('super_mega_fun_wizard')
|
||||
end
|
||||
|
||||
|
@ -156,7 +162,6 @@ describe CustomWizard::Action do
|
|||
action = CustomWizard::Action.new(
|
||||
wizard: wizard,
|
||||
action: open_composer,
|
||||
user: user,
|
||||
data: {}
|
||||
)
|
||||
action.perform
|
||||
|
@ -179,8 +184,7 @@ describe CustomWizard::Action do
|
|||
|
||||
it 'creates a group' do
|
||||
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
|
||||
wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
|
||||
expect(Group.where(name: wizard.current_submission['action_9']).exists?).to eq(true)
|
||||
end
|
||||
|
||||
|
@ -210,6 +214,6 @@ describe CustomWizard::Action do
|
|||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||
updater = wizard.create_updater(wizard.steps.last.id, {})
|
||||
updater.update
|
||||
expect(updater.result[:redirect_on_complete]).to eq("https://google.com")
|
||||
expect(updater.result[:redirect_on_next]).to eq("https://google.com")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,21 +16,35 @@ describe CustomWizard::Builder do
|
|||
fab!(:group) { Fabricate(:group) }
|
||||
|
||||
let(:required_data_json) {
|
||||
JSON.parse(File.open(
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/required_data.json"
|
||||
).read)
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
let(:permitted_json) {
|
||||
JSON.parse(File.open(
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
|
||||
).read)
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
let(:permitted_param_json) {
|
||||
JSON.parse(File.open(
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/permitted_params.json"
|
||||
).read)
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
let(:user_condition_json) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/condition/user_condition.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
before do
|
||||
|
@ -263,6 +277,23 @@ describe CustomWizard::Builder do
|
|||
expect(wizard.current_submission['saved_param']).to eq('param_value')
|
||||
end
|
||||
end
|
||||
|
||||
context "with condition" do
|
||||
before do
|
||||
@template[:steps][0][:condition] = user_condition_json['condition']
|
||||
CustomWizard::Template.save(@template.as_json)
|
||||
end
|
||||
|
||||
it "adds step when condition is passed" do
|
||||
wizard = CustomWizard::Builder.new(@template[:id], trusted_user).build
|
||||
expect(wizard.steps.first.id).to eq(@template[:steps][0]['id'])
|
||||
end
|
||||
|
||||
it "does not add step when condition is not passed" do
|
||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||
expect(wizard.steps.first.id).to eq(@template[:steps][1]['id'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'building field' do
|
||||
|
@ -284,6 +315,23 @@ describe CustomWizard::Builder do
|
|||
.fields.length
|
||||
).to eq(4)
|
||||
end
|
||||
|
||||
context "with condition" do
|
||||
before do
|
||||
@template[:steps][0][:fields][0][:condition] = user_condition_json['condition']
|
||||
CustomWizard::Template.save(@template.as_json)
|
||||
end
|
||||
|
||||
it "adds field when condition is passed" do
|
||||
wizard = CustomWizard::Builder.new(@template[:id], trusted_user).build
|
||||
expect(wizard.steps.first.fields.first.id).to eq(@template[:steps][0][:fields][0]['id'])
|
||||
end
|
||||
|
||||
it "does not add field when condition is not passed" do
|
||||
wizard = CustomWizard::Builder.new(@template[:id], user).build
|
||||
expect(wizard.steps.first.fields.first.id).to eq(@template[:steps][0][:fields][1]['id'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'on update' do
|
||||
|
|
|
@ -2,6 +2,12 @@
|
|||
require_relative '../../plugin_helper'
|
||||
|
||||
describe CustomWizard::Field 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
|
||||
|
||||
before do
|
||||
CustomWizard::Field.register(
|
||||
'location',
|
||||
|
@ -13,6 +19,19 @@ describe CustomWizard::Field do
|
|||
)
|
||||
end
|
||||
|
||||
it "initialize custom field attributes" do
|
||||
field = CustomWizard::Field.new(field_hash)
|
||||
expect(field.id).to eq("field_id")
|
||||
expect(field.index).to eq(0)
|
||||
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
|
||||
|
||||
it "registers custom field types" do
|
||||
expect(CustomWizard::Field.types[:location].present?).to eq(true)
|
||||
end
|
||||
|
|
36
spec/components/custom_wizard/step_spec.rb
Normale Datei
36
spec/components/custom_wizard/step_spec.rb
Normale Datei
|
@ -0,0 +1,36 @@
|
|||
# frozen_string_literal: true
|
||||
require_relative '../../plugin_helper'
|
||||
|
||||
describe CustomWizard::Step 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
|
||||
|
||||
let(:field_hash) do
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/field/field.json"
|
||||
).read
|
||||
).with_indifferent_access
|
||||
end
|
||||
|
||||
before do
|
||||
@step = CustomWizard::Step.new(step_hash[:id])
|
||||
end
|
||||
|
||||
it "adds fields" do
|
||||
@step.add_field(field_hash)
|
||||
expect(@step.fields.size).to eq(1)
|
||||
expect(@step.fields.first.index).to eq(0)
|
||||
end
|
||||
|
||||
it "adds fields with custom indexes" do
|
||||
field_hash[:index] = 2
|
||||
@step.add_field(field_hash)
|
||||
expect(@step.fields.first.index).to eq(2)
|
||||
end
|
||||
end
|
|
@ -1,4 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../../plugin_helper'
|
||||
|
||||
describe CustomWizard::Wizard do
|
||||
|
@ -7,27 +8,33 @@ describe CustomWizard::Wizard do
|
|||
fab!(:admin_user) { Fabricate(:user, admin: true) }
|
||||
|
||||
let(:template_json) {
|
||||
JSON.parse(File.open(
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read)
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
let(:permitted_json) {
|
||||
JSON.parse(File.open(
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
|
||||
).read)
|
||||
).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)
|
||||
end
|
||||
|
||||
def append_steps
|
||||
template_json['steps'].each do |step_template|
|
||||
@wizard.append_step(step_template['id'])
|
||||
end
|
||||
@wizard.update_step_order!
|
||||
end
|
||||
|
||||
def progress_step(step_id, acting_user: user, wizard: @wizard)
|
||||
|
@ -37,16 +44,48 @@ describe CustomWizard::Wizard do
|
|||
context: wizard.id,
|
||||
subject: step_id
|
||||
)
|
||||
@wizard.update_step_order!
|
||||
end
|
||||
|
||||
it "appends steps from a template" do
|
||||
it "appends steps" do
|
||||
append_steps
|
||||
expect(@wizard.steps.length).to eq(3)
|
||||
end
|
||||
|
||||
it "appends steps with indexes" do
|
||||
append_steps
|
||||
expect(@wizard.steps.first.index).to eq(0)
|
||||
expect(@wizard.steps.last.index).to eq(2)
|
||||
end
|
||||
|
||||
it "appends steps with custom indexes" do
|
||||
template_json['steps'][0]['index'] = 2
|
||||
template_json['steps'][1]['index'] = 1
|
||||
template_json['steps'][2]['index'] = 0
|
||||
|
||||
template_json['steps'].each do |step_template|
|
||||
@wizard.append_step(step_template['id']) do |step|
|
||||
step.index = step_template['index'] if step_template['index']
|
||||
end
|
||||
end
|
||||
|
||||
expect(@wizard.steps.first.index).to eq(2)
|
||||
expect(@wizard.steps.last.index).to eq(0)
|
||||
|
||||
@wizard.update_step_order!
|
||||
|
||||
expect(@wizard.steps.first.id).to eq("step_3")
|
||||
expect(@wizard.steps.last.id).to eq("step_1")
|
||||
|
||||
expect(@wizard.steps.first.next.id).to eq("step_2")
|
||||
expect(@wizard.steps.last.next).to eq(nil)
|
||||
end
|
||||
|
||||
it "determines the user's current step" do
|
||||
expect(@wizard.start.id).to eq('step_1')
|
||||
append_steps
|
||||
expect(@wizard.start).to eq('step_1')
|
||||
progress_step('step_1')
|
||||
expect(@wizard.start.id).to eq('step_2')
|
||||
expect(@wizard.start).to eq('step_2')
|
||||
end
|
||||
|
||||
it "creates a step updater" do
|
||||
|
@ -57,6 +96,7 @@ describe CustomWizard::Wizard do
|
|||
end
|
||||
|
||||
it "determines whether a wizard is unfinished" do
|
||||
append_steps
|
||||
expect(@wizard.unfinished?).to eq(true)
|
||||
progress_step("step_1")
|
||||
expect(@wizard.unfinished?).to eq(true)
|
||||
|
@ -67,6 +107,7 @@ describe CustomWizard::Wizard do
|
|||
end
|
||||
|
||||
it "determines whether a wizard has been completed by a user" do
|
||||
append_steps
|
||||
expect(@wizard.completed?).to eq(false)
|
||||
progress_step("step_1")
|
||||
progress_step("step_2")
|
||||
|
@ -75,6 +116,8 @@ describe CustomWizard::Wizard do
|
|||
end
|
||||
|
||||
it "is not completed if steps submitted before after time" do
|
||||
append_steps
|
||||
|
||||
progress_step("step_1")
|
||||
progress_step("step_2")
|
||||
progress_step("step_3")
|
||||
|
@ -83,7 +126,6 @@ describe CustomWizard::Wizard do
|
|||
template_json['after_time_scheduled'] = Time.now + 3.hours
|
||||
|
||||
wizard = CustomWizard::Wizard.new(template_json, user)
|
||||
|
||||
expect(wizard.completed?).to eq(false)
|
||||
end
|
||||
|
||||
|
@ -125,6 +167,8 @@ describe CustomWizard::Wizard do
|
|||
end
|
||||
|
||||
it "lets a permitted user access a complete wizard with multiple submissions" do
|
||||
append_steps
|
||||
|
||||
progress_step("step_1", acting_user: trusted_user)
|
||||
progress_step("step_2", acting_user: trusted_user)
|
||||
progress_step("step_3", acting_user: trusted_user)
|
||||
|
@ -135,6 +179,8 @@ describe CustomWizard::Wizard do
|
|||
end
|
||||
|
||||
it "does not let an unpermitted user access a complete wizard without multiple submissions" do
|
||||
append_steps
|
||||
|
||||
progress_step("step_1", acting_user: trusted_user)
|
||||
progress_step("step_2", acting_user: trusted_user)
|
||||
progress_step("step_3", acting_user: trusted_user)
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
require_relative '../plugin_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
|
|
@ -1,24 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
require_relative '../plugin_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
|
17
spec/fixtures/condition/user_condition.json
gevendort
Normale Datei
17
spec/fixtures/condition/user_condition.json
gevendort
Normale Datei
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"condition": [
|
||||
{
|
||||
"type": "validation",
|
||||
"pairs": [
|
||||
{
|
||||
"index": 0,
|
||||
"key": "username",
|
||||
"key_type": "user_field",
|
||||
"value": "angus",
|
||||
"value_type": "text",
|
||||
"connector": "equal"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
17
spec/fixtures/condition/wizard_field_condition.json
gevendort
Normale Datei
17
spec/fixtures/condition/wizard_field_condition.json
gevendort
Normale Datei
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"condition": [
|
||||
{
|
||||
"type": "validation",
|
||||
"pairs": [
|
||||
{
|
||||
"index": 0,
|
||||
"key": "step_1_field_1",
|
||||
"key_type": "wizard_field",
|
||||
"value": "Condition will pass",
|
||||
"value_type": "text",
|
||||
"connector": "equal"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
1
spec/fixtures/field/field.json
gevendort
1
spec/fixtures/field/field.json
gevendort
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"id": "field_id",
|
||||
"index": 0,
|
||||
"label": "Field Label",
|
||||
"image": "field_image_url.png",
|
||||
"description": "Field description",
|
||||
|
|
1
spec/fixtures/step/step.json
gevendort
1
spec/fixtures/step/step.json
gevendort
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"id": "step_1",
|
||||
"index": 0,
|
||||
"title": "Text",
|
||||
"description": "Step description",
|
||||
"image": "step_image_url.png",
|
||||
|
|
2
spec/fixtures/wizard.json
gevendort
2
spec/fixtures/wizard.json
gevendort
|
@ -535,7 +535,7 @@
|
|||
},
|
||||
{
|
||||
"id": "action_10",
|
||||
"run_after": "wizard_completion",
|
||||
"run_after": "step_3",
|
||||
"type": "route_to",
|
||||
"url": [
|
||||
{
|
||||
|
|
|
@ -11,12 +11,49 @@ describe CustomWizard::StepsController do
|
|||
)
|
||||
}
|
||||
|
||||
before do
|
||||
CustomWizard::Template.save(
|
||||
JSON.parse(File.open(
|
||||
fab!(:user2) {
|
||||
Fabricate(
|
||||
:user,
|
||||
username: 'bob',
|
||||
email: "bob@email.com",
|
||||
trust_level: TrustLevel[2]
|
||||
)
|
||||
}
|
||||
|
||||
let(:wizard_template) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read),
|
||||
skip_jobs: true)
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
let(:wizard_field_condition_template) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/condition/wizard_field_condition.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
let(:user_condition_template) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/condition/user_condition.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
let(:permitted_json) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
before do
|
||||
CustomWizard::Template.save(wizard_template, skip_jobs: true)
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
|
@ -27,17 +64,189 @@ describe CustomWizard::StepsController do
|
|||
}
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['wizard']['start']).to eq("step_2")
|
||||
|
||||
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")
|
||||
wizard_id = response.parsed_body['wizard']['id']
|
||||
wizard = CustomWizard::Wizard.create(wizard_id, user)
|
||||
expect(wizard.submissions.last['step_1_field_1']).to eq("Text input")
|
||||
end
|
||||
|
||||
context "raises an error" do
|
||||
it "when the wizard doesnt exist" do
|
||||
put '/w/not-super-mega-fun-wizard/steps/step_1.json'
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
|
||||
it "when the user cant access the wizard" do
|
||||
new_template = wizard_template.dup
|
||||
new_template["permitted"] = permitted_json["permitted"]
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json'
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
|
||||
it "when the step doesnt exist" do
|
||||
put '/w/super-mega-fun-wizard/steps/step_10.json'
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
|
||||
it "when user cant see the step due to conditions" do
|
||||
sign_in(user2)
|
||||
|
||||
new_wizard_template = wizard_template.dup
|
||||
new_wizard_template['steps'][0]['condition'] = user_condition_template['condition']
|
||||
CustomWizard::Template.save(new_wizard_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json'
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
end
|
||||
|
||||
it "works if the step has no fields" do
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json'
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['wizard']['start']).to eq("step_2")
|
||||
end
|
||||
|
||||
wizard = CustomWizard::Builder.new("super_mega_fun_wizard", user).build
|
||||
expect(wizard.start.id).to eq("step_2")
|
||||
it "returns an updated wizard when condition passes" do
|
||||
new_template = wizard_template.dup
|
||||
new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition will pass"
|
||||
}
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['wizard']['start']).to eq("step_2")
|
||||
end
|
||||
|
||||
it "returns an updated wizard when condition doesnt pass" do
|
||||
new_template = wizard_template.dup
|
||||
new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition wont pass"
|
||||
}
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['wizard']['start']).to eq("step_3")
|
||||
end
|
||||
|
||||
it "runs completion actions if user has completed wizard" do
|
||||
new_template = wizard_template.dup
|
||||
|
||||
## route_to action
|
||||
new_template['actions'].last['run_after'] = 'wizard_completion'
|
||||
new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
|
||||
new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition wont pass"
|
||||
}
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['redirect_on_complete']).to eq("https://google.com")
|
||||
end
|
||||
|
||||
it "saves results of completion actions if user has completed wizard" do
|
||||
new_template = wizard_template.dup
|
||||
|
||||
## Create group action
|
||||
new_template['actions'].first['run_after'] = 'wizard_completion'
|
||||
new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "My cool group"
|
||||
}
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_3.json'
|
||||
expect(response.status).to eq(200)
|
||||
|
||||
wizard_id = response.parsed_body['wizard']['id']
|
||||
wizard = CustomWizard::Wizard.create(wizard_id, user)
|
||||
group_name = wizard.submissions.last['action_9']
|
||||
group = Group.find_by(name: group_name)
|
||||
expect(group.full_name).to eq("My cool group")
|
||||
end
|
||||
|
||||
it "returns a final step without conditions" do
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json'
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(false)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_2.json'
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(false)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_3.json'
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(true)
|
||||
end
|
||||
|
||||
it "returns the correct final step when the conditional final step and last step are the same" do
|
||||
new_template = wizard_template.dup
|
||||
new_template['steps'][0]['condition'] = user_condition_template['condition']
|
||||
new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition will pass"
|
||||
}
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(false)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_2.json'
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(false)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_3.json'
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(true)
|
||||
end
|
||||
|
||||
it "returns the correct final step when the conditional final step and last step are different" do
|
||||
new_template = wizard_template.dup
|
||||
new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition will not pass"
|
||||
}
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(false)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_2.json'
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(true)
|
||||
end
|
||||
|
||||
it "returns the correct final step when the conditional final step is determined in the same action" do
|
||||
new_template = wizard_template.dup
|
||||
new_template['steps'][1]['condition'] = wizard_field_condition_template['condition']
|
||||
new_template['steps'][2]['condition'] = wizard_field_condition_template['condition']
|
||||
CustomWizard::Template.save(new_template, skip_jobs: true)
|
||||
|
||||
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
|
||||
fields: {
|
||||
step_1_field_1: "Condition will not pass"
|
||||
}
|
||||
}
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body['final']).to eq(true)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,9 +20,11 @@ describe CustomWizard::FieldSerializer do
|
|||
each_serializer: CustomWizard::FieldSerializer,
|
||||
scope: Guardian.new(user)
|
||||
).as_json
|
||||
expect(json_array.length).to eq(4)
|
||||
|
||||
expect(json_array.size).to eq(4)
|
||||
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)
|
||||
end
|
||||
|
||||
it "should return optional field attributes" do
|
||||
|
@ -32,7 +34,6 @@ describe CustomWizard::FieldSerializer do
|
|||
scope: Guardian.new(user)
|
||||
).as_json
|
||||
expect(json_array[0][:format]).to eq("YYYY-MM-DD")
|
||||
expect(json_array[3][:number]).to eq(4)
|
||||
expect(json_array[6][:file_types]).to eq(".jpg,.jpeg,.png")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../../plugin_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
|
67
spec/serializers/custom_wizard/wizard_step_serializer_spec.rb
Normale Datei
67
spec/serializers/custom_wizard/wizard_step_serializer_spec.rb
Normale Datei
|
@ -0,0 +1,67 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../../plugin_helper'
|
||||
|
||||
describe CustomWizard::StepSerializer do
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
|
||||
let(:wizard_template) {
|
||||
JSON.parse(
|
||||
File.open(
|
||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
|
||||
).read
|
||||
)
|
||||
}
|
||||
|
||||
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(wizard_template, 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: described_class,
|
||||
scope: Guardian.new(user)
|
||||
).as_json
|
||||
expect(json_array[0][:title]).to eq("<p>Text</p>")
|
||||
expect(json_array[0][:description]).to eq("<p>Text inputs!</p>")
|
||||
expect(json_array[1][:index]).to eq(1)
|
||||
end
|
||||
|
||||
it 'should return fields' do
|
||||
json_array = ActiveModel::ArraySerializer.new(
|
||||
@wizard.steps,
|
||||
each_serializer: described_class,
|
||||
scope: Guardian.new(user)
|
||||
).as_json
|
||||
expect(json_array[0][:fields].length).to eq(4)
|
||||
end
|
||||
|
||||
context 'with required data' do
|
||||
before do
|
||||
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)
|
||||
@wizard = CustomWizard::Builder.new("super_mega_fun_wizard", user).build
|
||||
end
|
||||
|
||||
it 'should return permitted attributes' do
|
||||
json_array = ActiveModel::ArraySerializer.new(
|
||||
@wizard.steps,
|
||||
each_serializer: described_class,
|
||||
scope: Guardian.new(user)
|
||||
).as_json
|
||||
expect(json_array[0][:permitted]).to eq(false)
|
||||
expect(json_array[0][:permitted_message]).to eq("Missing required data")
|
||||
end
|
||||
end
|
||||
end
|
Laden …
In neuem Issue referenzieren