diff --git a/app/controllers/custom_wizard/admin/wizard.rb b/app/controllers/custom_wizard/admin/wizard.rb index 955deb3f..993d0e6c 100644 --- a/app/controllers/custom_wizard/admin/wizard.rb +++ b/app/controllers/custom_wizard/admin/wizard.rb @@ -145,6 +145,8 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController custom_fields: mapped_params, visible: mapped_params, required: mapped_params, + poster: mapped_params, + guest_email: mapped_params, recipient: mapped_params, categories: mapped_params, mute_remainder: mapped_params, diff --git a/assets/javascripts/discourse/components/custom-wizard-no-access.js.es6 b/assets/javascripts/discourse/components/custom-wizard-no-access.js.es6 index b3b2e26c..13ec0611 100644 --- a/assets/javascripts/discourse/components/custom-wizard-no-access.js.es6 +++ b/assets/javascripts/discourse/components/custom-wizard-no-access.js.es6 @@ -3,6 +3,8 @@ import discourseComputed from "discourse-common/utils/decorators"; import Component from "@ember/component"; import { dasherize } from "@ember/string"; import getURL from "discourse-common/lib/get-url"; +import cookie from "discourse/lib/cookie"; +import { getOwner } from "@ember/application"; export default Component.extend({ classNameBindings: [":wizard-no-access", "reasonClass"], @@ -17,6 +19,11 @@ export default Component.extend({ return this.siteSettings.title || ""; }, + @discourseComputed("reason") + showLoginButton(reason) { + return reason === "requiresLogin"; + }, + actions: { skip() { if (this.currentUser) { @@ -25,5 +32,10 @@ export default Component.extend({ window.location = getURL("/"); } }, + + showLogin() { + cookie("destination_url", getURL(`/w/${this.get("wizardId")}`)); + getOwner(this).lookup("route:application").send("showLogin"); + }, }, }); diff --git a/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6 b/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6 index eb9e735a..cfa243e7 100644 --- a/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6 +++ b/assets/javascripts/discourse/components/wizard-mapper-selector.js.es6 @@ -121,6 +121,13 @@ export default Component.extend({ guestGroup: computed("options.guestGroup", "inputType", function () { return this.optionEnabled("guestGroup"); }), + includeMessageableGroups: computed( + "options.includeMessageableGroups", + "inputType", + function () { + return this.optionEnabled("includeMessageableGroups"); + } + ), userEnabled: computed("options.userSelection", "inputType", function () { return this.optionEnabled("userSelection"); }), @@ -352,6 +359,17 @@ export default Component.extend({ return result; }, + @discourseComputed("includeMessageableGroups", "options.userLimit") + userOptions(includeMessageableGroups, userLimit) { + const opts = { + includeMessageableGroups, + }; + if (userLimit) { + opts.maximum = userLimit; + } + return opts; + }, + optionEnabled(type) { const options = this.options; if (!options) { diff --git a/assets/javascripts/discourse/components/wizard-mapper.js.es6 b/assets/javascripts/discourse/components/wizard-mapper.js.es6 index ec58e3f2..aad6a0e6 100644 --- a/assets/javascripts/discourse/components/wizard-mapper.js.es6 +++ b/assets/javascripts/discourse/components/wizard-mapper.js.es6 @@ -33,6 +33,8 @@ export default Component.extend({ outputConnector: options.outputConnector || null, context: options.context || null, guestGroup: options.guestGroup || false, + includeMessageableGroups: options.includeMessageableGroups || false, + userLimit: options.userLimit || null, }; let inputTypes = ["key", "value", "output"]; diff --git a/assets/javascripts/discourse/components/wizard-user-chooser.js.es6 b/assets/javascripts/discourse/components/wizard-user-chooser.js.es6 new file mode 100644 index 00000000..a177a1eb --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-user-chooser.js.es6 @@ -0,0 +1,58 @@ +import UserChooserComponent from "select-kit/components/user-chooser"; +import I18n from "I18n"; + +export const WIZARD_USER = "wizard-user"; + +export default UserChooserComponent.extend({ + pluginApiIdentifiers: ["wizard-user-chooser"], + classNames: ["user-chooser", "wizard-user-chooser"], + classNameBindings: ["selectKit.options.fullWidthWrap:full-width-wrap"], + valueProperty: "id", + nameProperty: "name", + + modifyComponentForRow() { + return "wizard-user-chooser/wizard-user-chooser-row"; + }, + + modifyNoSelection() { + return this.defaultItem( + WIZARD_USER, + I18n.t("admin.wizard.action.poster.wizard_user") + ); + }, + + selectKitOptions: { + fullWidthWrap: false, + autoWrap: false, + }, + + search() { + const superPromise = this._super(...arguments); + if (!superPromise) { + return; + } + return superPromise.then((results) => { + if (!results || results.length === 0) { + return; + } + return results.map((item) => { + const reconstructed = {}; + if (item.username) { + reconstructed.id = item.username; + if (item.username.includes("@")) { + reconstructed.isEmail = true; + } else { + reconstructed.isUser = true; + reconstructed.name = item.name; + reconstructed.showUserStatus = this.showUserStatus; + } + } else if (item.name) { + reconstructed.id = item.name; + reconstructed.name = item.full_name; + reconstructed.isGroup = true; + } + return { ...item, ...reconstructed }; + }); + }); + }, +}); diff --git a/assets/javascripts/discourse/components/wizard-user-chooser/wizard-user-chooser-row.js b/assets/javascripts/discourse/components/wizard-user-chooser/wizard-user-chooser-row.js new file mode 100644 index 00000000..125bccca --- /dev/null +++ b/assets/javascripts/discourse/components/wizard-user-chooser/wizard-user-chooser-row.js @@ -0,0 +1,5 @@ +import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row"; + +export default SelectKitRowComponent.extend({ + classNames: ["user-row", "wizard-user-chooser-row"], +}); diff --git a/assets/javascripts/discourse/lib/wizard-schema.js.es6 b/assets/javascripts/discourse/lib/wizard-schema.js.es6 index 710a3594..01175ebb 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js.es6 +++ b/assets/javascripts/discourse/lib/wizard-schema.js.es6 @@ -100,6 +100,8 @@ const action = { custom_fields: null, skip_redirect: null, suppress_notifications: null, + poster: "wizard-user", + guest_email: null, add_event: null, add_location: null, }, @@ -111,6 +113,8 @@ const action = { skip_redirect: null, custom_fields: null, required: null, + poster: "wizard-user", + guest_email: null, recipient: null, suppress_notifications: null, }, @@ -184,6 +188,7 @@ const action = { "custom_fields", "required", "recipient", + "poster", "profile_updates", "group", "url", diff --git a/assets/javascripts/discourse/templates/components/custom-wizard-no-access.hbs b/assets/javascripts/discourse/templates/components/custom-wizard-no-access.hbs index cb9f59f1..30af5d77 100644 --- a/assets/javascripts/discourse/templates/components/custom-wizard-no-access.hbs +++ b/assets/javascripts/discourse/templates/components/custom-wizard-no-access.hbs @@ -1,11 +1,14 @@
{{text}}
- + + {{#if showLoginButton}} + + {{/if}}
\ No newline at end of file diff --git a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs index 80152674..9c9898f9 100644 --- a/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-custom-action.hbs @@ -217,6 +217,7 @@ userSelection="output" outputDefaultSelection="user" context="action" + includeMessageableGroups="true" ) }} @@ -919,6 +920,48 @@ {{/if}} {{#if showPostAdvanced}} +
+
+ +
+ +
+ {{wizard-mapper + inputs=this.action.poster + property="poster" + onUpdate=(action "mappedFieldUpdated") + options=(hash + textSelection="key,value" + wizardFieldSelection=true + userSelection="output" + outputDefaultSelection="user" + userLimit="1" + context="action" + ) + }} +
+
+ +
+
+ +
+ +
+ {{wizard-mapper + inputs=this.action.guest_email + property="guest_email" + onUpdate=(action "mappedFieldUpdated") + options=(hash + textSelection="key,value" + wizardFieldSelection=true + outputPlaceholder="admin.wizard.action.guest_email.placeholder" + context="action" + ) + }} +
+
+
diff --git a/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs b/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs index 80669aa4..3971cd26 100644 --- a/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs +++ b/assets/javascripts/discourse/templates/components/wizard-mapper-selector.hbs @@ -66,12 +66,12 @@ {{/if}} {{#if showUser}} - {{email-group-user-chooser + {{wizard-user-chooser placeholderKey=placeholderKey value=value autocomplete="discourse" onChange=(action "changeUserValue") - options=(hash includeMessageableGroups="true") + options=userOptions }} {{/if}}
\ No newline at end of file diff --git a/assets/javascripts/discourse/templates/components/wizard-user-chooser/wizard-user-chooser-row.hbs b/assets/javascripts/discourse/templates/components/wizard-user-chooser/wizard-user-chooser-row.hbs new file mode 100644 index 00000000..8ff700b0 --- /dev/null +++ b/assets/javascripts/discourse/templates/components/wizard-user-chooser/wizard-user-chooser-row.hbs @@ -0,0 +1,20 @@ +{{#if this.item.isUser}} + {{avatar this.item imageSize="tiny"}} +
+ {{format-username this.item.id}} + {{this.item.name}} +
+ {{#if (and this.item.showUserStatus this.item.status)}} + + {{/if}} + {{decorate-username-selector this.item.id}} +{{else if this.item.isGroup}} + {{d-icon "users"}} +
+ {{this.item.id}} + {{this.item.full_name}} +
+{{else}} + {{d-icon "envelope"}} + {{this.item.id}} +{{/if}} \ No newline at end of file diff --git a/assets/stylesheets/common/wizard/wizard.scss b/assets/stylesheets/common/wizard/wizard.scss index 4c8f2357..e4a8ef77 100644 --- a/assets/stylesheets/common/wizard/wizard.scss +++ b/assets/stylesheets/common/wizard/wizard.scss @@ -158,7 +158,12 @@ body.custom-wizard { .no-access-gutter { margin-top: 10px; display: flex; - justify-content: flex-end; + justify-content: space-between; + + .return-to-site { + display: flex; + align-items: center; + } } .powered-by-discourse { diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 71d5eeac..15a78ed3 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -5,7 +5,7 @@ en: completed: "You have completed this wizard." not_permitted: "You are not permitted to access this wizard." none: "There is no wizard here." - return_to_site: "Return to {{siteName}}" + return_to_site: "Return to {{siteName}}." requires_login: "You need to be logged in to access this wizard." reset: "Reset this wizard." step_not_permitted: "You're not permitted to view this step." @@ -344,6 +344,12 @@ en: include: "Include Fields" title: "Title" post: "Post" + poster: + label: "Poster" + wizard_user: "Wizard user" + guest_email: + label: "Guest email" + placeholder: "Field for guest email" topic_attr: "Topic Attribute" interpolate_fields: "Insert wizard fields using the field_id in w{}. Insert user fields using field key in u{}." diff --git a/lib/custom_wizard/action.rb b/lib/custom_wizard/action.rb index 9fe725e7..d539d785 100644 --- a/lib/custom_wizard/action.rb +++ b/lib/custom_wizard/action.rb @@ -7,13 +7,14 @@ class CustomWizard::Action :result REQUIRES_USER = %w[ - create_topic update_profile open_composer watch_categories add_to_group ] + WIZARD_USER = 'wizard-user' + def initialize(opts) @wizard = opts[:wizard] @action = opts[:action] @@ -69,7 +70,7 @@ class CustomWizard::Action end if params[:title].present? && params[:raw].present? - creator = PostCreator.new(user, params) + creator = PostCreator.new(topic_poster, params) post = creator.create if creator.errors.present? @@ -119,25 +120,26 @@ class CustomWizard::Action params[:target_group_names] = [] params[:target_usernames] = [] + params[:target_emails] = [] [*targets].each do |target| if Group.find_by(name: target) params[:target_group_names] << target elsif User.find_by_username(target) params[:target_usernames] << target - else - # + elsif target.match(/@/) # Compare discourse/discourse/app/controllers/posts_controller.rb#L922-L923 + params[:target_emails] << target end end if params[:title].present? && params[:raw].present? && (params[:target_usernames].present? || - params[:target_group_names].present?) + params[:target_group_names].present? || + params[:target_emails].present?) params[:archetype] = Archetype.private_message - poster = user || Discourse.system_user - creator = PostCreator.new(poster, params) + creator = PostCreator.new(topic_poster, params) post = creator.create if creator.errors.present? @@ -651,6 +653,45 @@ class CustomWizard::Action params end + def topic_poster + @topic_poster ||= begin + poster_id = CustomWizard::Mapper.new( + inputs: action['poster'], + data: mapper_data, + user: user, + ).perform + poster_id = [*poster_id].first if poster_id.present? + + if poster_id.blank? || poster_id === WIZARD_USER + poster = user || guest_user + else + poster = User.find_by_username(poster_id) + end + + poster || Discourse.system_user + end + end + + def guest_user + @guest_user ||= begin + return nil unless action['guest_email'] + + email = CustomWizard::Mapper.new( + inputs: action['guest_email'], + data: mapper_data, + ).perform + + if email&.match(/@/) + User.create!( + email: email, + username: UserNameSuggester.suggest(email), + name: User.suggest_name(email), + staged: true, + ) + end + end + end + def new_group_params params = {} diff --git a/lib/custom_wizard/field.rb b/lib/custom_wizard/field.rb index e8e7bbf9..c0ebaae3 100644 --- a/lib/custom_wizard/field.rb +++ b/lib/custom_wizard/field.rb @@ -31,7 +31,6 @@ class CustomWizard::Field :step REQUIRES_USER = %w[ - composer upload ] diff --git a/plugin.rb b/plugin.rb index 3358fe97..f70cbc59 100644 --- a/plugin.rb +++ b/plugin.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # name: discourse-custom-wizard # about: Forms for Discourse. Better onboarding, structured posting, data enrichment, automated actions and much more. -# version: 2.7.1 +# version: 2.8.0 # authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George, Kaitlin Maddever, Juan Marcos Gutierrez Ramos # url: https://github.com/paviliondev/discourse-custom-wizard # contact_emails: development@pavilion.tech diff --git a/spec/components/custom_wizard/action_spec.rb b/spec/components/custom_wizard/action_spec.rb index 03deab27..13f1b13f 100644 --- a/spec/components/custom_wizard/action_spec.rb +++ b/spec/components/custom_wizard/action_spec.rb @@ -46,7 +46,7 @@ describe CustomWizard::Action do update_template(wizard_template) end - context 'creating a topic' do + describe '#create_topic' do it "works" do wizard = CustomWizard::Builder.new(@template[:id], user).build wizard.create_updater( @@ -160,6 +160,43 @@ describe CustomWizard::Action do expect(action.result.success?).to eq(true) expect(TopicCustomField.exists?(name: custom_field_name)).to eq(true) end + + it "allows poster to be set" do + wizard_template[:actions][0]["poster"] = [ + { + "type": "assignment", + "output_type": "user", + "output_connector": "set", + "output": [ + "angus1" + ] + } + ] + update_template(wizard_template) + + wizard = CustomWizard::Builder.new(@template[:id], user).build + wizard.create_updater( + wizard.steps.first.id, + step_1_field_1: "Topic Title", + step_1_field_2: "topic body" + ).update + wizard.create_updater(wizard.steps.second.id, {}).update + wizard.create_updater(wizard.steps.last.id, + step_3_field_3: category.id + ).update + + topic = Topic.where( + title: "Topic Title", + category_id: category.id + ) + expect(topic.exists?).to eq(true) + post = Post.find_by( + topic_id: topic.pluck(:id), + raw: "topic body" + ) + expect(post.present?).to eq(true) + expect(post.user.username).to eq('angus1') + end end it 'updates a profile' do @@ -234,6 +271,7 @@ describe CustomWizard::Action do context "standard subscription actions" do before do enable_subscription("standard") + Jobs.run_immediately! end it 'watches tags' do @@ -333,26 +371,117 @@ describe CustomWizard::Action do expect(user2.reload.notifications.count).to eq(1) end - it "send_message works with guests are permitted" do - wizard_template["permitted"] = guests_permitted["permitted"] - wizard_template.delete("actions") - wizard_template['actions'] = [send_message] - update_template(wizard_template) + context "with a guest" do + describe "#create_topic" do + it "creates a staged guest poster if guest_email is set" do + Jobs.run_immediately! - User.create(username: 'angus1', email: "angus1@email.com") + wizard_template["permitted"] = guests_permitted["permitted"] + wizard_template[:steps][0][:fields] << { + "id": "step_1_field_5", + "label": "Guest Email", + "type": "text", + "min_length": "3", + }.as_json + create_topic["run_after"] = "step_3" + create_topic["guest_email"] = [ + { + "type": "assignment", + "output": "step_1_field_5", + "output_type": "wizard_field", + "output_connector": "set" + } + ] + create_topic["category"] = [ + { + "type": "assignment", + "output": "step_3_field_3", + "output_type": "wizard_field", + "output_connector": "set" + } + ] + wizard_template.delete("actions") + wizard_template[:actions] = [create_topic] - wizard = CustomWizard::Builder.new(wizard_template["id"], nil, CustomWizard::Wizard.generate_guest_id).build - wizard.create_updater(wizard.steps[0].id, {}).update - updater = wizard.create_updater(wizard.steps[1].id, {}) - updater.update + update_template(wizard_template) - topic = Topic.where(archetype: Archetype.private_message, title: "Message title") - post = Post.where(topic_id: topic.pluck(:id)) + wizard = CustomWizard::Builder.new( + @template[:id], + nil, + CustomWizard::Wizard.generate_guest_id + ).build + wizard.create_updater( + wizard.steps.first.id, + step_1_field_5: "guest@email.com" + ).update + wizard.create_updater(wizard.steps.second.id, {}).update + wizard.create_updater(wizard.steps.last.id, + step_3_field_3: category.id + ).update - expect(topic.exists?).to eq(true) - expect(topic.first.topic_allowed_users.first.user.username).to eq('angus1') - expect(topic.first.topic_allowed_users.second.user.username).to eq(Discourse.system_user.username) - expect(post.exists?).to eq(true) + topic = Topic.where(category_id: category.id).first + expect(topic.present?).to eq(true) + expect(topic.posts.first.user.staged).to eq(true) + expect(topic.posts.first.user.primary_email.email).to eq('guest@email.com') + end + end + + describe "#send_message" do + it "works" do + wizard_template["permitted"] = guests_permitted["permitted"] + wizard_template.delete("actions") + wizard_template['actions'] = [send_message] + update_template(wizard_template) + + wizard = CustomWizard::Builder.new(wizard_template["id"], nil, CustomWizard::Wizard.generate_guest_id).build + wizard.create_updater(wizard.steps[0].id, {}).update + updater = wizard.create_updater(wizard.steps[1].id, {}) + updater.update + + topic = Topic.where(archetype: Archetype.private_message, title: "Message title") + post = Post.where(topic_id: topic.pluck(:id)) + + expect(topic.exists?).to eq(true) + expect(topic.first.topic_allowed_users.first.user.username).to eq(user1.username) + expect(topic.first.topic_allowed_users.second.user.username).to eq(Discourse.system_user.username) + expect(post.exists?).to eq(true) + end + + it "works when the target is an email address" do + Jobs.run_immediately! + + wizard_template["permitted"] = guests_permitted["permitted"] + wizard_template.delete("actions") + + send_message["recipient"] = [ + { + "type": "assignment", + "output": "step_1_field_1", + "output_type": "wizard_field", + "output_connector": "set" + } + ] + + wizard_template['actions'] = [send_message] + update_template(wizard_template) + + NotificationEmailer.expects(:process_notification).once + + wizard = CustomWizard::Builder.new(wizard_template["id"], nil, CustomWizard::Wizard.generate_guest_id).build + wizard.create_updater(wizard.steps[0].id, step_1_field_1: "guest@email.com").update + updater = wizard.create_updater(wizard.steps[1].id, {}) + updater.update + + topic = Topic.where(archetype: Archetype.private_message, title: "Message title") + post = Post.where(topic_id: topic.pluck(:id)) + + expect(topic.exists?).to eq(true) + expect(topic.first.topic_allowed_users.first.user.staged).to eq(true) + expect(topic.first.topic_allowed_users.first.user.primary_email.email).to eq('guest@email.com') + expect(topic.first.topic_allowed_users.second.user.username).to eq(Discourse.system_user.username) + expect(post.exists?).to eq(true) + end + end end end @@ -493,7 +622,6 @@ describe CustomWizard::Action do wizard.create_updater(wizard.steps.second.id, {}).update wizard.create_updater(wizard.steps.last.id, step_3_field_3: category.id) .update - User.create(username: 'angus1', email: 'angus1@email.com') wizard.create_updater(wizard.steps[0].id, {}).update wizard.create_updater(wizard.steps[1].id, {}).update topic = Topic.where(title: 'Topic Title', category_id: category.id) diff --git a/spec/components/custom_wizard/template_validator_spec.rb b/spec/components/custom_wizard/template_validator_spec.rb index fe61be91..38288409 100644 --- a/spec/components/custom_wizard/template_validator_spec.rb +++ b/spec/components/custom_wizard/template_validator_spec.rb @@ -155,9 +155,6 @@ describe CustomWizard::TemplateValidator do validator = CustomWizard::TemplateValidator.new(template) expect(validator.perform).to eq(false) errors = validator.errors.to_a - expect(errors).to include( - I18n.t("wizard.validation.not_permitted_for_guests", object_id: "action_1") - ) expect(errors).to include( I18n.t("wizard.validation.not_permitted_for_guests", object_id: "step_2_field_7") ) diff --git a/test/javascripts/acceptance/admin-wizards-business-subscription-test.js b/test/javascripts/acceptance/admin-wizards-business-subscription-test.js index e7c2f337..33bbbed9 100644 --- a/test/javascripts/acceptance/admin-wizards-business-subscription-test.js +++ b/test/javascripts/acceptance/admin-wizards-business-subscription-test.js @@ -221,7 +221,7 @@ acceptance("Admin | Custom Wizard Business Subscription", function (needs) { ".admin-wizard-container .wizard-custom-action .setting" ); assert.ok( - listTopicSettings.length === 10, + listTopicSettings.length === 12, "Display all settings of create topic" ); await actionTypeDropdown.expand(); diff --git a/test/javascripts/acceptance/admin-wizards-standard-subscription-test.js b/test/javascripts/acceptance/admin-wizards-standard-subscription-test.js index 7cc05c5c..66aeac1a 100644 --- a/test/javascripts/acceptance/admin-wizards-standard-subscription-test.js +++ b/test/javascripts/acceptance/admin-wizards-standard-subscription-test.js @@ -215,7 +215,7 @@ acceptance("Admin | Custom Wizard Standard Subscription", function (needs) { ".admin-wizard-container .wizard-custom-action .setting" ); assert.ok( - listTopicSettings.length === 10, + listTopicSettings.length === 12, "Display all settings of create topic" ); await actionTypeDropdown.expand(); @@ -224,7 +224,7 @@ acceptance("Admin | Custom Wizard Standard Subscription", function (needs) { ".admin-wizard-container .wizard-custom-action .setting" ); assert.ok( - listTopicSettings.length === 9, + listTopicSettings.length === 11, "Display all settings of send message" ); await actionTypeDropdown.expand(); diff --git a/test/javascripts/acceptance/admin-wizards-unsubscribed-test.js b/test/javascripts/acceptance/admin-wizards-unsubscribed-test.js index 55c456c7..b98b3e1c 100644 --- a/test/javascripts/acceptance/admin-wizards-unsubscribed-test.js +++ b/test/javascripts/acceptance/admin-wizards-unsubscribed-test.js @@ -396,7 +396,7 @@ acceptance("Admin | Custom Wizard Unsubscribed", function (needs) { ".admin-wizard-container .wizard-custom-action .setting" ); assert.ok( - listTopicSettings.length === 10, + listTopicSettings.length === 12, "Display all settings of create topic" ); await actionTypeDropdown.expand();