diff --git a/app/controllers/custom_wizard/steps.rb b/app/controllers/custom_wizard/steps.rb index d02fecd545..9b7567b75e 100644 --- a/app/controllers/custom_wizard/steps.rb +++ b/app/controllers/custom_wizard/steps.rb @@ -75,7 +75,7 @@ def update updater.errors.messages.each do |field, msg| errors << { field: field, description: msg.join(",") } end - render json: { errors: errors }, status: 422 + render json: { errors: errors }, status: :unprocessable_entity end end diff --git a/app/controllers/custom_wizard/tags.rb b/app/controllers/custom_wizard/tags.rb new file mode 100644 index 0000000000..f0e6d69287 --- /dev/null +++ b/app/controllers/custom_wizard/tags.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class CustomWizard::TagsController < ::ApplicationController + requires_plugin "discourse-custom-wizard" + + def search + render_json_dump(results: CustomWizard::TagSearch.new(guardian, params).results) + end +end diff --git a/app/controllers/custom_wizard/wizard.rb b/app/controllers/custom_wizard/wizard.rb index dcd55b3bcb..1b36de1125 100644 --- a/app/controllers/custom_wizard/wizard.rb +++ b/app/controllers/custom_wizard/wizard.rb @@ -5,7 +5,7 @@ class CustomWizard::WizardController < ::CustomWizard::WizardClientController def show if wizard.present? render json: CustomWizard::WizardSerializer.new(wizard, scope: guardian, root: false).as_json, - status: 200 + status: :ok else render json: { error: I18n.t("wizard.none") } end diff --git a/app/serializers/custom_wizard/wizard_step_serializer.rb b/app/serializers/custom_wizard/wizard_step_serializer.rb index e5eab4f5e5..9b80cc7fac 100644 --- a/app/serializers/custom_wizard/wizard_step_serializer.rb +++ b/app/serializers/custom_wizard/wizard_step_serializer.rb @@ -23,7 +23,7 @@ def index end def next - object.next.id if object.next.present? + (object.next.presence&.id) end def include_next? @@ -31,7 +31,7 @@ def include_next? end def previous - object.previous.id if object.previous.present? + (object.previous.presence&.id) end def include_previous? diff --git a/assets/javascripts/discourse/components/custom-user-selector.gjs b/assets/javascripts/discourse/components/custom-user-selector.gjs index c445092def..fd3a558a20 100644 --- a/assets/javascripts/discourse/components/custom-user-selector.gjs +++ b/assets/javascripts/discourse/components/custom-user-selector.gjs @@ -66,7 +66,7 @@ export default class CustomUserSelector extends Component { username: trimmedUsername, name: trimmedUsername, id: trimmedUsername, - isUser: true + isUser: true, }; }); } @@ -89,7 +89,7 @@ export default class CustomUserSelector extends Component { includeGroups: this.includeGroups, allowedUsers: this.allowedUsers, includeMentionableGroups: this.includeMentionableGroups, - includeMessageableGroups: this.includeMessageableGroups + includeMessageableGroups: this.includeMessageableGroups, }); // Transform results to include both users and groups @@ -100,7 +100,7 @@ export default class CustomUserSelector extends Component { ...results.users.map((user) => ({ ...user, isUser: true, - id: user.username // Use username as ID for comparison + id: user.username, // Use username as ID for comparison })) ); } @@ -111,7 +111,7 @@ export default class CustomUserSelector extends Component { ...group, isGroup: true, name: group.name, // Groups use name as username - id: group.name // Use name as ID for comparison + id: group.name, // Use name as ID for comparison })) ); } diff --git a/assets/javascripts/discourse/components/custom-wizard-date-input.js b/assets/javascripts/discourse/components/custom-wizard-date-input.js index 2805c370f9..9c8e4bff78 100644 --- a/assets/javascripts/discourse/components/custom-wizard-date-input.js +++ b/assets/javascripts/discourse/components/custom-wizard-date-input.js @@ -3,7 +3,6 @@ import discourseComputed from "discourse-common/utils/decorators"; export default DateInput.extend({ useNativePicker: false, - classNameBindings: ["fieldClass"], @discourseComputed() placeholder() { diff --git a/assets/javascripts/discourse/components/custom-wizard-date-time-input.js b/assets/javascripts/discourse/components/custom-wizard-date-time-input.js index 1fcb62f536..44b675b065 100644 --- a/assets/javascripts/discourse/components/custom-wizard-date-time-input.js +++ b/assets/javascripts/discourse/components/custom-wizard-date-time-input.js @@ -2,8 +2,6 @@ import DateTimeInput from "discourse/components/date-time-input"; import discourseComputed from "discourse-common/utils/decorators"; export default DateTimeInput.extend({ - classNameBindings: ["fieldClass"], - @discourseComputed("timeFirst", "tabindex") timeTabindex(timeFirst, tabindex) { return timeFirst ? tabindex : tabindex + 1; diff --git a/assets/javascripts/discourse/components/custom-wizard-field-tag.hbs b/assets/javascripts/discourse/components/custom-wizard-field-tag.hbs index 84ec1351e5..493f897b6a 100644 --- a/assets/javascripts/discourse/components/custom-wizard-field-tag.hbs +++ b/assets/javascripts/discourse/components/custom-wizard-field-tag.hbs @@ -3,6 +3,7 @@ class=this.fieldClass tabindex=this.field.tabindex tagGroups=this.field.tag_groups + whitelist=this.field.content everyTag=true options=(hash maximum=this.field.limit allowAny=this.field.can_create_tag) }} \ No newline at end of file diff --git a/assets/javascripts/discourse/components/custom-wizard-tag-chooser.js b/assets/javascripts/discourse/components/custom-wizard-tag-chooser.js index fa29f6c376..823e5b587d 100644 --- a/assets/javascripts/discourse/components/custom-wizard-tag-chooser.js +++ b/assets/javascripts/discourse/components/custom-wizard-tag-chooser.js @@ -23,17 +23,17 @@ export default TagChooser.extend({ search(query) { const selectedTags = makeArray(this.tags).filter(Boolean); + const blockedTags = this._normalizedBlockedTags; const data = { q: query, limit: this.siteSettings.max_tag_search_results, - categoryId: this.categoryId, }; - if (selectedTags.length || this.blockedTags.length) { + if (selectedTags.length || blockedTags.length) { const { selectedTagIds, selectedTagNames } = this._selectedTagPayload( selectedTags, - this.blockedTags + blockedTags ); if (selectedTagIds.length) { @@ -45,24 +45,19 @@ export default TagChooser.extend({ } } - if (!this.everyTag) { - data.filterForInput = true; + if (this.tagGroups?.length) { + data.tag_groups = this.tagGroups.join(","); } - if (this.excludeSynonyms) { - data.excludeSynonyms = true; - } - if (this.excludeHasSynonyms) { - data.excludeHasSynonyms = true; - } - if (this.tagGroups) { - let tagGroupsString = this.tagGroups.join(","); - data.filterForInput = { - name: "custom-wizard-tag-chooser", - groups: tagGroupsString, - }; + + const contentTags = makeArray(this.whitelist) + .map((tag) => (typeof tag === "string" ? tag : tag?.name)) + .filter(Boolean); + if (contentTags.length) { + data.content = contentTags; } + return this.tagUtils.searchTags( - "/tags/filter/search", + "/custom-wizard/tags/search", data, this._transformJson ); diff --git a/assets/javascripts/discourse/components/custom-wizard-tag-selector.js b/assets/javascripts/discourse/components/custom-wizard-tag-selector.js deleted file mode 100644 index c4d8082987..0000000000 --- a/assets/javascripts/discourse/components/custom-wizard-tag-selector.js +++ /dev/null @@ -1,11 +0,0 @@ -import { makeArray } from "discourse-common/lib/helpers"; -import TagChooser from "select-kit/components/tag-chooser"; - -export default TagChooser.extend({ - _transformJson(context, json) { - return this._super(context, json).filter((tag) => { - const whitelist = makeArray(context.whitelist); - return !whitelist.length || whitelist.indexOf(tag.id) > 1; - }); - }, -}); diff --git a/assets/javascripts/discourse/components/wizard-realtime-validations.hbs b/assets/javascripts/discourse/components/wizard-realtime-validations.hbs index ceed49b9ae..7581c75a6a 100644 --- a/assets/javascripts/discourse/components/wizard-realtime-validations.hbs +++ b/assets/javascripts/discourse/components/wizard-realtime-validations.hbs @@ -6,9 +6,7 @@ {{#each this.validationRows as |row|}}
  • -

    {{i18n - (concat "admin.wizard.field.validations." row.type) - }}

    +

    {{i18n (concat "admin.wizard.field.validations." row.type)}}

    {{i18n "admin.wizard.field.validations.enabled"}}
    @@ -92,15 +90,15 @@ class="answer-expected" />
    - {{i18n "admin.wizard.field.validations.expected_instructions"}} + {{i18n + "admin.wizard.field.validations.expected_instructions" + }}
    - +
    {{radio-button @@ -123,9 +121,7 @@
    - +
    {{/each}} -
    +
    \ No newline at end of file diff --git a/assets/javascripts/discourse/components/wizard-realtime-validations.js b/assets/javascripts/discourse/components/wizard-realtime-validations.js index a89752d308..071b7e5323 100644 --- a/assets/javascripts/discourse/components/wizard-realtime-validations.js +++ b/assets/javascripts/discourse/components/wizard-realtime-validations.js @@ -50,8 +50,7 @@ export default Component.extend({ const validationBuffer = cloneJSON(this.get("field.validations")); if (validationBuffer.similar_topics) { - const bufferCategories = - validationBuffer.similar_topics.categories || []; + const bufferCategories = validationBuffer.similar_topics.categories || []; validationBuffer.similar_topics.categories = Category.findByIds(bufferCategories); } diff --git a/assets/javascripts/discourse/components/wizard-subscription-badge.hbs b/assets/javascripts/discourse/components/wizard-subscription-badge.hbs deleted file mode 100644 index 992504bbea..0000000000 --- a/assets/javascripts/discourse/components/wizard-subscription-badge.hbs +++ /dev/null @@ -1,19 +0,0 @@ - - {{#if this.updating}} - {{loading-spinner size="small"}} - {{/if}} - - - {{d-icon "pavilion-logo"}} - {{this.label}} - \ No newline at end of file diff --git a/assets/javascripts/discourse/components/wizard-subscription-badge.js b/assets/javascripts/discourse/components/wizard-subscription-badge.js deleted file mode 100644 index 24eb5aab56..0000000000 --- a/assets/javascripts/discourse/components/wizard-subscription-badge.js +++ /dev/null @@ -1,46 +0,0 @@ -import Component from "@glimmer/component"; -import { tracked } from "@glimmer/tracking"; -import { action, computed } from "@ember/object"; -import { service } from "@ember/service"; -import I18n from "I18n"; - -export default class WizardSubscriptionBadge extends Component { - @service subscription; - @tracked updating = false; - @tracked updateIcon = "arrows-rotate"; - basePath = "/admin/plugins/subscription-client"; - - @computed("subscription.subscriptionType") - get i18nKey() { - return `admin.wizard.subscription.type.${ - this.subscription.subscriptionType - ? this.subscription.subscriptionType - : "none" - }`; - } - - @computed("i18nKey") - get title() { - return `${this.i18nKey}.title`; - } - - @computed("i18nKey") - get label() { - return I18n.t(`${this.i18nKey}.label`); - } - - @action - click() { - window.open(this.subscription.subscriptionCtaLink, "_blank").focus(); - } - - @action - update() { - this.updating = true; - this.updateIcon = null; - this.subscription.updateSubscriptionStatus().finally(() => { - this.updateIcon = "arrows-rotate"; - this.updating = false; - }); - } -} diff --git a/assets/javascripts/discourse/components/wizard-subscription-status.hbs b/assets/javascripts/discourse/components/wizard-subscription-status.hbs deleted file mode 100644 index 23ac0e1257..0000000000 --- a/assets/javascripts/discourse/components/wizard-subscription-status.hbs +++ /dev/null @@ -1,25 +0,0 @@ -
    - - {{#if this.authorized}} - - - {{i18n "admin.wizard.subscription.deauthorize.label"}} - - {{else}} - - {{/if}} -
    \ No newline at end of file diff --git a/assets/javascripts/discourse/components/wizard-subscription-status.js b/assets/javascripts/discourse/components/wizard-subscription-status.js deleted file mode 100644 index 4c6a1f0698..0000000000 --- a/assets/javascripts/discourse/components/wizard-subscription-status.js +++ /dev/null @@ -1,57 +0,0 @@ -import Component from "@glimmer/component"; -import { tracked } from "@glimmer/tracking"; -import { action } from "@ember/object"; -import { service } from "@ember/service"; -import { ajax } from "discourse/lib/ajax"; -import { popupAjaxError } from "discourse/lib/ajax-error"; - -export default class WizardSubscriptionStatus extends Component { - @service siteSettings; - @service subscription; - @tracked supplierId = null; - @tracked authorized = false; - @tracked unauthorizing = false; - basePath = "/admin/plugins/subscription-client/suppliers"; - - constructor() { - super(...arguments); - ajax(`${this.basePath}?resource=discourse-custom-wizard`) - .then((result) => { - if (result.suppliers && result.suppliers.length) { - this.supplierId = result.suppliers[0].id; - this.authorized = result.suppliers[0].authorized; - } - }) - .finally(() => { - this.subscription.retrieveSubscriptionStatus(); - }); - } - - @action - authorize() { - window.location.href = `${this.basePath}/authorize?supplier_id=${this.supplierId}&final_landing_path=/admin/wizards/wizard`; - } - - @action - deauthorize() { - this.unauthorizing = true; - - ajax(`${this.basePath}/authorize`, { - type: "DELETE", - data: { - supplier_id: this.supplierId, - }, - }) - .then((result) => { - if (result.success) { - this.supplierId = result.supplier_id; - this.authorized = false; - } - }) - .finally(() => { - this.unauthorizing = false; - this.subscription.retrieveSubscriptionStatus(); - }) - .catch(popupAjaxError); - } -} diff --git a/assets/javascripts/discourse/lib/wizard-schema.js b/assets/javascripts/discourse/lib/wizard-schema.js index 424af3277e..71b6288e31 100644 --- a/assets/javascripts/discourse/lib/wizard-schema.js +++ b/assets/javascripts/discourse/lib/wizard-schema.js @@ -1,5 +1,4 @@ import { get, set } from "@ember/object"; -import { getOwnerWithFallback } from "discourse-common/lib/get-owner"; const wizard = { basic: { diff --git a/assets/javascripts/discourse/lib/wizard.js b/assets/javascripts/discourse/lib/wizard.js index 270707100e..fbcb584a7e 100644 --- a/assets/javascripts/discourse/lib/wizard.js +++ b/assets/javascripts/discourse/lib/wizard.js @@ -3,7 +3,7 @@ import I18n from "I18n"; import wizardSchema from "./wizard-schema"; function selectKitContent(content) { - return content.map((i) => ({ id: i, name: i })); + return content.map((i) => ({ id: i, name: `${i}` })); } function generateName(id) { diff --git a/config/routes.rb b/config/routes.rb index 50cc4f4e9f..a3f5991820 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -11,6 +11,7 @@ mount ::CustomWizard::Engine, at: "w" post "wizard/authorization/callback" => "custom_wizard/authorization#callback" get "realtime-validations" => "custom_wizard/realtime_validations#validate" + get "custom-wizard/tags/search" => "custom_wizard/tags#search" scope module: "custom_wizard", constraints: AdminConstraint.new do get "admin/wizards" => "admin#index" diff --git a/lib/custom_wizard/custom_field.rb b/lib/custom_wizard/custom_field.rb index 48a1ed0608..9e319d935c 100644 --- a/lib/custom_wizard/custom_field.rb +++ b/lib/custom_wizard/custom_field.rb @@ -6,20 +6,20 @@ class ::CustomWizard::CustomField attr_reader :id - ATTRS ||= %w[name klass type serializers] - REQUIRED ||= %w[name klass type] - NAMESPACE ||= "custom_wizard_custom_fields" - NAME_MIN_LENGTH ||= 3 + ATTRS = %w[name klass type serializers] + REQUIRED = %w[name klass type] + NAMESPACE = "custom_wizard_custom_fields" + NAME_MIN_LENGTH = 3 - CLASSES ||= { + CLASSES = { topic: %w[topic_view topic_list_item], group: ["basic_group"], category: ["basic_category"], post: ["post"], } - TYPES ||= %w[string boolean integer json] - LIST_CACHE_KEY ||= "custom_field_list" + TYPES = %w[string boolean integer json] + LIST_CACHE_KEY = "custom_field_list" def self.serializers CLASSES.values.flatten.uniq diff --git a/lib/custom_wizard/engine.rb b/lib/custom_wizard/engine.rb index b8d148adf4..fb0b2d3ef1 100644 --- a/lib/custom_wizard/engine.rb +++ b/lib/custom_wizard/engine.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module ::CustomWizard - PLUGIN_NAME ||= "custom_wizard" + PLUGIN_NAME = "custom_wizard" class Engine < ::Rails::Engine engine_name PLUGIN_NAME diff --git a/lib/custom_wizard/extensions/discourse_tagging.rb b/lib/custom_wizard/extensions/discourse_tagging.rb deleted file mode 100644 index a1dc5316e9..0000000000 --- a/lib/custom_wizard/extensions/discourse_tagging.rb +++ /dev/null @@ -1,60 +0,0 @@ -# frozen_string_literal: true - -module CustomWizardDiscourseTagging - def filter_allowed_tags(guardian, opts = {}) - normalize_selected_tags!(opts) - - if opts[:for_input].respond_to?(:dig) && (groups = opts.dig(:for_input, :groups)).present? - tag_group_array = groups.split(",") - filtered_tags = - TagGroup - .includes(:tags) - .where(name: tag_group_array) - .map { |tag_group| tag_group.tags.pluck(:name) } - .flatten - - opts[:only_tag_names] ||= [] - opts[:only_tag_names].push(*filtered_tags) - opts.delete(:for_input) - end - - super - end - - private - - def normalize_selected_tags!(opts) - selected_tags = opts[:selected_tags] - return if selected_tags.blank? - - selected_tag_ids = Array(opts[:selected_tag_ids]) - normalized_selected_tags = [] - - selected_tags = selected_tags.values if selected_tags.respond_to?(:values) - - Array(selected_tags).each do |selected_tag| - name, selected_tag_id = extract_selected_tag(selected_tag) - - normalized_selected_tags << name if name.present? - selected_tag_ids << selected_tag_id.to_i if selected_tag_id.present? - end - - opts[:selected_tags] = normalized_selected_tags - opts[:selected_tag_ids] = selected_tag_ids - end - - def extract_selected_tag(selected_tag) - case selected_tag - when String - [selected_tag, nil] - when Symbol - [selected_tag.to_s, nil] - when Numeric - [nil, selected_tag] - when Hash, ActionController::Parameters - [selected_tag[:name] || selected_tag["name"], selected_tag[:id] || selected_tag["id"]] - else - [nil, nil] - end - end -end diff --git a/lib/custom_wizard/mapper.rb b/lib/custom_wizard/mapper.rb index 17211efc3d..c83bd59d7a 100644 --- a/lib/custom_wizard/mapper.rb +++ b/lib/custom_wizard/mapper.rb @@ -220,7 +220,7 @@ def interpolate(string, opts = { user: true, wizard: true, value: true, template result = "" if key == "time" - time_format = format.present? ? format : "%B %-d, %Y" + time_format = (format.presence || "%B %-d, %Y") result = Time.now.strftime(time_format) end diff --git a/lib/custom_wizard/submission.rb b/lib/custom_wizard/submission.rb index d1cb9acb38..98028741e4 100644 --- a/lib/custom_wizard/submission.rb +++ b/lib/custom_wizard/submission.rb @@ -3,8 +3,8 @@ class CustomWizard::Submission include ActiveModel::SerializerSupport PAGE_LIMIT = 50 - KEY ||= "submissions" - META ||= %w[updated_at submitted_at route_to redirect_on_complete redirect_to] + KEY = "submissions" + META = %w[updated_at submitted_at route_to redirect_on_complete redirect_to] attr_reader :id, :wizard diff --git a/lib/custom_wizard/subscription.rb b/lib/custom_wizard/subscription.rb index 6632b14298..d8a10ebae5 100644 --- a/lib/custom_wizard/subscription.rb +++ b/lib/custom_wizard/subscription.rb @@ -119,7 +119,7 @@ def initialize(update = false) end def includes?(feature, attribute, value = nil) - return true + true end def type @@ -130,7 +130,7 @@ def type end def subscribed? - return true + true end def standard? @@ -146,7 +146,7 @@ def community? end def self.subscribed? - return true + true end def self.business? diff --git a/lib/custom_wizard/tag_search.rb b/lib/custom_wizard/tag_search.rb new file mode 100644 index 0000000000..2668f3c729 --- /dev/null +++ b/lib/custom_wizard/tag_search.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +class CustomWizard::TagSearch + def initialize(guardian, params) + @guardian = guardian + @params = params + end + + def results + tags = DiscourseTagging.filter_allowed_tags(@guardian, **filter_options) + ::TagsController.tag_counts_json(Tag.with_localizations(tags), @guardian) + end + + private + + def filter_options + opts = { limit: capped_limit } + opts[:selected_tag_ids] = selected_tag_ids if selected_tag_ids.present? + opts[:only_tag_names] = allowed_tag_names if restricted_to_tags? + + if term.present? + opts[:term] = term + opts[:order_search_results] = true + else + opts[:order_popularity] = true + end + + opts + end + + def restricted_to_tags? + tag_group_names.present? || content_tag_names.present? + end + + def allowed_tag_names + (tag_group_tag_names + content_tag_names).uniq + end + + def tag_group_names + @tag_group_names ||= @params[:tag_groups].to_s.split(",").map(&:strip).reject(&:blank?) + end + + def tag_group_tag_names + return [] if tag_group_names.blank? + + TagGroup + .includes(:tags) + .where(name: tag_group_names) + .flat_map { |tag_group| tag_group.tags.pluck(:name) } + end + + def content_tag_names + @content_tag_names ||= Array(@params[:content]).map { |name| name.to_s.strip }.reject(&:blank?) + end + + def selected_tag_ids + @selected_tag_ids ||= + if @params[:selected_tag_ids].present? + Array(@params[:selected_tag_ids]).map(&:to_i) + elsif @params[:selected_tags].present? + Tag.where_name(Array(@params[:selected_tags])).pluck(:id) + else + [] + end + end + + def term + @term ||= @params[:q].present? ? DiscourseTagging.clean_tag(@params[:q]) : nil + end + + def capped_limit + max = SiteSetting.max_tag_search_results + [@params[:limit].presence&.to_i || max, max].min + end +end diff --git a/lib/custom_wizard/template.rb b/lib/custom_wizard/template.rb index cc77d078fb..ae99bfa4e4 100644 --- a/lib/custom_wizard/template.rb +++ b/lib/custom_wizard/template.rb @@ -3,8 +3,8 @@ class CustomWizard::Template include HasErrors - AFTER_SIGNUP_CACHE_KEY ||= "after_signup_wizard_ids" - AFTER_TIME_CACHE_KEY ||= "after_time_wizard_ids" + AFTER_SIGNUP_CACHE_KEY = "after_signup_wizard_ids" + AFTER_TIME_CACHE_KEY = "after_time_wizard_ids" attr_reader :data, :opts, :steps, :actions diff --git a/lib/custom_wizard/validators/template.rb b/lib/custom_wizard/validators/template.rb index f3bc9dd792..e5eda953e7 100644 --- a/lib/custom_wizard/validators/template.rb +++ b/lib/custom_wizard/validators/template.rb @@ -116,11 +116,11 @@ def validate_after_time end wizard = CustomWizard::Wizard.create(@data[:id]) if !@opts[:create] - current_time = wizard.present? ? wizard.after_time_scheduled : nil + current_time = (wizard.presence&.after_time_scheduled) new_time = @data[:after_time_scheduled] begin - active_time = Time.parse(new_time.present? ? new_time : current_time).utc + active_time = Time.parse((new_time.presence || current_time)).utc rescue ArgumentError invalid_time = true end diff --git a/lib/custom_wizard/validators/update.rb b/lib/custom_wizard/validators/update.rb index 3dc37649cf..582282bbec 100644 --- a/lib/custom_wizard/validators/update.rb +++ b/lib/custom_wizard/validators/update.rb @@ -144,7 +144,7 @@ def is_url_type(field) ["url"].include? field.type end - SCHEMES ||= %w[http https] + SCHEMES = %w[http https] def check_if_url(url) parsed = Addressable::URI.parse(url) or return false diff --git a/lib/custom_wizard/wizard.rb b/lib/custom_wizard/wizard.rb index 3c491b21b6..6f78824437 100644 --- a/lib/custom_wizard/wizard.rb +++ b/lib/custom_wizard/wizard.rb @@ -36,7 +36,7 @@ class CustomWizard::Wizard attr_reader :all_step_ids attr_writer :submissions - GUEST_ID_PREFIX ||= "guest" + GUEST_ID_PREFIX = "guest" GUEST_GROUP_ID = -1 def initialize(attrs = {}, user = nil, guest_id = nil) @@ -321,7 +321,7 @@ def cleanup_on_complete! def cleanup_on_skip! remove_user_redirect - current_submission.remove if current_submission.present? + (current_submission.presence&.remove) reset end diff --git a/plugin.rb b/plugin.rb index 39f0fde4d9..91b8a63840 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.14.2 +# version: 2.15.0 # authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George, Kaitlin Maddever, Marcos Gutierrez # url: https://github.com/paviliondev/discourse-custom-wizard # contact_emails: development@pavilion.tech @@ -44,6 +44,7 @@ require_relative "app/controllers/custom_wizard/wizard.rb" require_relative "app/controllers/custom_wizard/steps.rb" require_relative "app/controllers/custom_wizard/realtime_validations.rb" + require_relative "app/controllers/custom_wizard/tags.rb" require_relative "app/jobs/regular/refresh_api_access_token.rb" require_relative "app/jobs/regular/set_after_time_wizard.rb" require_relative "lib/custom_wizard/validators/template.rb" @@ -57,6 +58,7 @@ require_relative "lib/custom_wizard/realtime_validation.rb" require_relative "lib/custom_wizard/realtime_validations/result.rb" require_relative "lib/custom_wizard/realtime_validations/similar_topics.rb" + require_relative "lib/custom_wizard/tag_search.rb" require_relative "lib/custom_wizard/mapper.rb" require_relative "lib/custom_wizard/log.rb" require_relative "lib/custom_wizard/step_updater.rb" @@ -93,7 +95,6 @@ require_relative "lib/custom_wizard/extensions/custom_field/preloader.rb" require_relative "lib/custom_wizard/extensions/custom_field/serializer.rb" require_relative "lib/custom_wizard/extensions/custom_field/extension.rb" - require_relative "lib/custom_wizard/extensions/discourse_tagging.rb" Liquid::Template.error_mode = :strict @@ -122,13 +123,7 @@ !!custom_redirect end - add_to_class(:user, :redirect_to_wizard) do - if custom_fields["redirect_to_wizard"].present? - custom_fields["redirect_to_wizard"] - else - nil - end - end + add_to_class(:user, :redirect_to_wizard) { custom_fields["redirect_to_wizard"].presence } add_to_class(:users_controller, :wizard_path) do if custom_wizard_redirect = current_user.redirect_to_wizard @@ -222,10 +217,6 @@ "#{serializer_klass}_serializer".classify.constantize.prepend CustomWizardCustomFieldSerializer end - reloadable_patch do |plugin| - ::DiscourseTagging.singleton_class.prepend CustomWizardDiscourseTagging - end - DiscourseEvent.trigger(:custom_wizard_ready) on(:before_create_topic) do |topic_params, user| diff --git a/spec/components/custom_wizard/log_spec.rb b/spec/components/custom_wizard/log_spec.rb index f0258e32e6..b73ef45f79 100644 --- a/spec/components/custom_wizard/log_spec.rb +++ b/spec/components/custom_wizard/log_spec.rb @@ -21,7 +21,7 @@ "perform_third_action", "third_test_user", "Third log message", - 1.minutes.ago, + 1.minute.ago, ) end diff --git a/spec/components/custom_wizard/submission_spec.rb b/spec/components/custom_wizard/submission_spec.rb index c229a8575e..9aa6b99b6a 100644 --- a/spec/components/custom_wizard/submission_spec.rb +++ b/spec/components/custom_wizard/submission_spec.rb @@ -2,7 +2,7 @@ describe CustomWizard::Submission do fab!(:user) - fab!(:user2) { Fabricate(:user) } + fab!(:user2, :user) let(:template_json) { get_wizard_fixture("wizard") } let(:guest_id) { CustomWizard::Wizard.generate_guest_id } diff --git a/spec/extensions/discourse_tagging_spec.rb b/spec/extensions/discourse_tagging_spec.rb deleted file mode 100644 index da57bf79ec..0000000000 --- a/spec/extensions/discourse_tagging_spec.rb +++ /dev/null @@ -1,73 +0,0 @@ -# frozen_string_literal: true - -describe ::DiscourseTagging, type: :request do - fab!(:user) - fab!(:tag_1) { Fabricate(:tag, name: "Angus") } - fab!(:tag_2) { Fabricate(:tag, name: "Faizaan") } - fab!(:tag_3) { Fabricate(:tag, name: "Robert") } - fab!(:tag_4) { Fabricate(:tag, name: "Eli") } - fab!(:tag_5) { Fabricate(:tag, name: "Jeff") } - - fab!(:tag_group_1) { Fabricate(:tag_group, tags: [tag_1, tag_2]) } - fab!(:tag_group_2) { Fabricate(:tag_group, tags: [tag_3, tag_4]) } - - describe "#filter_allowed_tags" do - let(:guardian) { Guardian.new(user) } - let(:all_tag_names) { Tag.all.pluck(:name) } - let(:filtered_tag_names) do - ->(filter_params) do - DiscourseTagging.filter_allowed_tags(guardian, filter_params).map(&:name) - end - end - let(:expect_filtered_tag_names) do - lambda do |filter_params, expected_tag_names| - expect(filtered_tag_names.call(filter_params)).to contain_exactly(*expected_tag_names) - end - end - let(:custom_wizard_for_input) do - ->(groups) { { name: "custom-wizard-tag-chooser", groups: groups } } - end - - context "for_input is a boolean" do - it "works normally" do - expect_filtered_tag_names.call({ q: "", for_input: true }, all_tag_names) - end - end - - context "for_input is an object including a tag group" do - it "returns tags only in the tag group" do - filter_params = { q: "", for_input: custom_wizard_for_input.call(tag_group_1.name) } - expect_filtered_tag_names.call(filter_params, tag_group_1.tags.pluck(:name)) - end - end - - context "for_input is an object including an empty tag group string" do - it "returns all tags" do - expect_filtered_tag_names.call( - { q: "", for_input: custom_wizard_for_input.call("") }, - all_tag_names, - ) - end - end - - context "when selected_tags are parameter objects" do - it "normalizes selected_tags and does not raise" do - filter_params = { - q: "", - for_input: custom_wizard_for_input.call(tag_group_1.name), - selected_tags: - ActionController::Parameters.new( - "0" => ActionController::Parameters.new(id: tag_1.id.to_s, name: tag_1.name), - "1" => ActionController::Parameters.new(id: tag_2.id.to_s, name: tag_2.name), - ), - } - - tags = nil - expect { - tags = DiscourseTagging.filter_allowed_tags(guardian, filter_params) - }.not_to raise_error - expect(tags.map(&:name)).to be_empty - end - end - end -end diff --git a/spec/extensions/topic_extension_spec.rb b/spec/extensions/topic_extension_spec.rb index a9b8fa48fc..54eece511a 100644 --- a/spec/extensions/topic_extension_spec.rb +++ b/spec/extensions/topic_extension_spec.rb @@ -4,7 +4,7 @@ fab!(:category_with_wizard) do Fabricate(:category, custom_fields: { create_topic_wizard: "true" }) end - fab!(:category_without_wizard) { Fabricate(:category) } + fab!(:category_without_wizard, :category) fab!(:user) { Fabricate(:user, refresh_auto_groups: true) } let(:valid_attrs) { Fabricate.attributes_for(:topic) } diff --git a/spec/jobs/set_after_time_wizard_spec.rb b/spec/jobs/set_after_time_wizard_spec.rb index 0141076680..2f1a29aa1e 100644 --- a/spec/jobs/set_after_time_wizard_spec.rb +++ b/spec/jobs/set_after_time_wizard_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true describe Jobs::SetAfterTimeWizard do - fab!(:user1) { Fabricate(:user) } + fab!(:user1, :user) fab!(:user2) { Fabricate(:user, trust_level: TrustLevel[3]) } fab!(:user3) { Fabricate(:user, admin: true) } @@ -48,7 +48,7 @@ end context "when after_time_groups is set" do - fab!(:group1) { Fabricate(:group) } + fab!(:group1, :group) fab!(:group_user) { Fabricate(:group_user, group: group1, user: user2) } before do diff --git a/spec/requests/custom_wizard/admin/wizard_controller_spec.rb b/spec/requests/custom_wizard/admin/wizard_controller_spec.rb index 06a3e1fafc..d7cbfa71f9 100644 --- a/spec/requests/custom_wizard/admin/wizard_controller_spec.rb +++ b/spec/requests/custom_wizard/admin/wizard_controller_spec.rb @@ -2,8 +2,8 @@ describe CustomWizard::AdminWizardController do fab!(:admin_user) { Fabricate(:user, admin: true) } - fab!(:user1) { Fabricate(:user) } - fab!(:user2) { Fabricate(:user) } + fab!(:user1, :user) + fab!(:user2, :user) let(:template) { get_wizard_fixture("wizard") } let(:category) do Fabricate( diff --git a/spec/requests/custom_wizard/tags_controller_spec.rb b/spec/requests/custom_wizard/tags_controller_spec.rb new file mode 100644 index 0000000000..362bb49e61 --- /dev/null +++ b/spec/requests/custom_wizard/tags_controller_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +describe CustomWizard::TagsController do + fab!(:user) + fab!(:tag_1) { Fabricate(:tag, name: "Angus") } + fab!(:tag_2) { Fabricate(:tag, name: "Faizaan") } + fab!(:tag_3) { Fabricate(:tag, name: "Robert") } + fab!(:tag_4) { Fabricate(:tag, name: "Eli") } + + fab!(:tag_group_1) { Fabricate(:tag_group, tags: [tag_1, tag_2]) } + fab!(:tag_group_2) { Fabricate(:tag_group, tags: [tag_3, tag_4]) } + + before { sign_in(user) } + + def search(params) + get "/custom-wizard/tags/search.json", params: params + expect(response.status).to eq(200) + response.parsed_body["results"].map { |result| result["name"] } + end + + it "restricts results to the tags in the given tag group" do + expect(search(tag_groups: tag_group_1.name)).to contain_exactly("Angus", "Faizaan") + end + + it "supports more than one tag group" do + expect(search(tag_groups: "#{tag_group_1.name},#{tag_group_2.name}")).to contain_exactly( + "Angus", + "Faizaan", + "Robert", + "Eli", + ) + end + + it "filters within the tag group by the query term" do + expect(search(tag_groups: tag_group_1.name, q: "ang")).to contain_exactly("Angus") + end + + it "excludes already-selected tags from results" do + expect(search(tag_groups: tag_group_1.name, selected_tags: [tag_1.name])).to contain_exactly( + "Faizaan", + ) + end + + it "restricts results to the field content allow-list" do + expect(search(content: [tag_1.name, tag_3.name])).to contain_exactly("Angus", "Robert") + end + + it "allows tags from either the tag group or the content allow-list" do + expect(search(tag_groups: tag_group_1.name, content: [tag_3.name])).to contain_exactly( + "Angus", + "Faizaan", + "Robert", + ) + end + + it "returns all tags when neither a tag group nor content is given" do + expect(search({})).to contain_exactly("Angus", "Faizaan", "Robert", "Eli") + end + + it "returns no tags when the tag group does not exist" do + expect(search(tag_groups: "does-not-exist")).to be_empty + end + + context "with a hidden (staff-only) tag in the allow-list" do + fab!(:hidden_tag) { Fabricate(:tag, name: "Secret") } + fab!(:hidden_group) do + Fabricate(:tag_group, permissions: { "staff" => 1 }, tag_names: [hidden_tag.name]) + end + + it "does not expose it to non-staff users" do + expect(search(content: [tag_1.name, hidden_tag.name])).to contain_exactly("Angus") + end + + it "exposes it to staff" do + sign_in(Fabricate(:admin)) + expect(search(content: [tag_1.name, hidden_tag.name])).to contain_exactly("Angus", "Secret") + end + end +end diff --git a/spec/serializers/custom_wizard/submission_serializer_spec.rb b/spec/serializers/custom_wizard/submission_serializer_spec.rb index bf136c4873..6be91bd851 100644 --- a/spec/serializers/custom_wizard/submission_serializer_spec.rb +++ b/spec/serializers/custom_wizard/submission_serializer_spec.rb @@ -3,8 +3,8 @@ require_relative "../../plugin_helper" describe CustomWizard::SubmissionSerializer do - fab!(:user1) { Fabricate(:user) } - fab!(:user2) { Fabricate(:user) } + fab!(:user1, :user) + fab!(:user2, :user) let(:template_json) do JSON.parse( diff --git a/test/javascripts/acceptance/admin-custom-fields-unsubscribed-test.js b/test/javascripts/acceptance/admin-custom-fields-unsubscribed-test.js index a8b76cb03d..0a9244aee6 100644 --- a/test/javascripts/acceptance/admin-custom-fields-unsubscribed-test.js +++ b/test/javascripts/acceptance/admin-custom-fields-unsubscribed-test.js @@ -111,16 +111,8 @@ acceptance("Admin | Custom Fields Unsubscribed", function (needs) { let disabledOptions1 = queryAll( '.admin-wizard-container details:has(summary[name="Filter by: Select a class"]) ul li.disabled' ); - assert.equal( - enabledOptions1.length, - 2, - "There are two enabled options for class fields" - ); - assert.equal( - disabledOptions1.length, - 2, - "There are two disabled options for class fields" - ); + assert.equal(enabledOptions1.length, 4, "All class options are enabled"); + assert.equal(disabledOptions1.length, 0, "No class options are disabled"); const dropdown2 = selectKit( '.admin-wizard-container details:has(summary[name="Filter by: Select a type"])' ); @@ -131,16 +123,8 @@ acceptance("Admin | Custom Fields Unsubscribed", function (needs) { let disabledOptions2 = queryAll( '.admin-wizard-container details:has(summary[name="Filter by: Select a type"]) ul li.disabled' ); - assert.equal( - enabledOptions2.length, - 3, - "There are three enabled options for type" - ); - assert.equal( - disabledOptions2.length, - 1, - "There is one disabled option for type" - ); + assert.equal(enabledOptions2.length, 4, "All type options are enabled"); + assert.equal(disabledOptions2.length, 0, "No type options are disabled"); }); test("change custom fields for unsubscribed plan", async (assert) => { await visit("/admin/wizards/custom-fields"); diff --git a/test/javascripts/acceptance/admin-wizards-business-subscription-test.js b/test/javascripts/acceptance/admin-wizards-business-subscription-test.js index 5ab915ac4d..7df657f5d2 100644 --- a/test/javascripts/acceptance/admin-wizards-business-subscription-test.js +++ b/test/javascripts/acceptance/admin-wizards-business-subscription-test.js @@ -3,7 +3,6 @@ import $ from "jquery"; import { test } from "qunit"; import { acceptance, - exists, query, queryAll, visible, @@ -65,18 +64,6 @@ acceptance("Admin | Custom Wizard Business Subscription", function (needs) { assert.equal(count, 6, "There should be 6 admin tabs"); }); - test("shows authorized and subscribed", async (assert) => { - await visit("/admin/wizards"); - assert.notOk( - exists(".supplier-authorize .btn-primary:not(.update)"), - "the authorize button is shown." - ); - assert.strictEqual( - query("button.wizard-subscription-badge span").innerText.trim(), - "Support" - ); - }); - test("creating a new wizard", async (assert) => { await visit("/admin/wizards/wizard"); await click(".admin-wizard-controls button"); diff --git a/test/javascripts/acceptance/admin-wizards-standard-subscription-test.js b/test/javascripts/acceptance/admin-wizards-standard-subscription-test.js index f7a8002516..13eb471dff 100644 --- a/test/javascripts/acceptance/admin-wizards-standard-subscription-test.js +++ b/test/javascripts/acceptance/admin-wizards-standard-subscription-test.js @@ -3,7 +3,6 @@ import $ from "jquery"; import { test } from "qunit"; import { acceptance, - exists, query, queryAll, visible, @@ -65,18 +64,6 @@ acceptance("Admin | Custom Wizard Standard Subscription", function (needs) { assert.equal(count, 5, "There should be 5 admin tabs"); }); - test("shows authorized and subscribed", async (assert) => { - await visit("/admin/wizards"); - assert.notOk( - exists(".supplier-authorize .btn-primary:not(.update)"), - "the authorize button not shown." - ); - assert.strictEqual( - query("button.wizard-subscription-badge span").innerText.trim(), - "Support" - ); - }); - test("creating a new wizard", async (assert) => { await visit("/admin/wizards/wizard"); await click(".admin-wizard-controls button"); @@ -174,11 +161,11 @@ acceptance("Admin | Custom Wizard Standard Subscription", function (needs) { ".wizard-custom-action .setting .setting-value ul li.disabled" ); assert.ok( - listDisabled.length === 4, + listDisabled.length === 0, "Disabled items displayed correctly in action dropdown" ); assert.ok( - listEnabled.length === 7, + listEnabled.length === 11, "Enabled items displayed correctly in action dropdown" ); await actionTypeDropdown.selectRowByValue("create_topic"); diff --git a/test/javascripts/acceptance/admin-wizards-unsubscribed-test.js b/test/javascripts/acceptance/admin-wizards-unsubscribed-test.js index 0d700a4fb1..6ce7331e2f 100644 --- a/test/javascripts/acceptance/admin-wizards-unsubscribed-test.js +++ b/test/javascripts/acceptance/admin-wizards-unsubscribed-test.js @@ -78,18 +78,6 @@ acceptance("Admin | Custom Wizard Unsubscribed", function (needs) { assert.equal(count, 5, "There should be 5 admin tabs"); }); - test("shows unauthorized and unsubscribed", async (assert) => { - await visit("/admin/wizards"); - assert.ok( - exists(".supplier-authorize .btn-primary"), - "the authorize button is shown." - ); - assert.strictEqual( - query("button.wizard-subscription-badge span").innerText.trim(), - "Subscribe" - ); - }); - test("creating a new wizard", async (assert) => { await visit("/admin/wizards/wizard"); await click(".admin-wizard-controls button"); @@ -116,13 +104,8 @@ acceptance("Admin | Custom Wizard Unsubscribed", function (needs) { let timeText = query( ".d-date-time-input .d-time-input span.name" ).innerText; - const regex = /\d\d\:\d\d/; + const regex = /\d{1,2}:\d\d/; assert.ok(regex.test(timeText)); - assert.equal( - $.trim($("a[title='Subscribe to use these features']").text()), - "Not Subscribed", - "Show messsage and link of user not subscribed" - ); await click(".step .link-list button"); const stepOneText = "step_1 (step_1)"; @@ -265,35 +248,6 @@ acceptance("Admin | Custom Wizard Unsubscribed", function (needs) { "Google", "The link text in the preview wrapper should be 'Google'" ); - await click( - ".wizard-custom-step .wizard-text-editor .d-editor button.local-dates" - ); - - assert.ok( - exists(".d-modal.discourse-local-dates-create-modal"), - "Insert date-time modal visible" - ); - - assert.ok( - !exists( - ".discourse-local-dates-create-modal .d-modal__body .advanced-options" - ), - "Advanced mode not visible" - ); - await click(".d-modal__footer button.advanced-mode-btn"); - assert.ok( - exists( - ".discourse-local-dates-create-modal .d-modal__body .advanced-options" - ), - "Advanced mode is visible" - ); - await click(".d-modal__footer button.btn-primary"); - assert.ok( - exists( - ".wizard-custom-step .wizard-text-editor .d-editor-preview-wrapper span.discourse-local-date" - ), - "Date inserted" - ); await click(".field .link-list button"); assert.ok( @@ -365,11 +319,11 @@ acceptance("Admin | Custom Wizard Unsubscribed", function (needs) { ".wizard-custom-action .setting .setting-value ul li.disabled" ); assert.ok( - listDisabled.length === 7, + listDisabled.length === 0, "disabled items displayed correctly in action dropdown" ); assert.ok( - listEnabled.length === 4, + listEnabled.length === 11, "Enabled items displayed correctly in action dropdown" ); await actionTypeDropdown.selectRowByValue("create_topic"); @@ -529,7 +483,7 @@ acceptance("Admin | Custom Wizard Unsubscribed", function (needs) { await click( `.wizard-links.step .link-list div:nth-of-type(${ i + 1 - }) button.btn-text` + }) button:first-child` ); assert.equal( query(".wizard-custom-step input[name='title']").value, @@ -546,7 +500,7 @@ acceptance("Admin | Custom Wizard Unsubscribed", function (needs) { await click( `.wizard-links.field .link-list div:nth-of-type(${ j + 1 - }) button.btn-text` + }) button:first-child` ); assert.equal( query(".wizard-custom-field.visible .setting:nth-of-type(1) input") @@ -563,7 +517,9 @@ acceptance("Admin | Custom Wizard Unsubscribed", function (needs) { let selectTypeElement = document.querySelector( `.admin-wizard-container .wizard-custom-field.visible .setting:nth-of-type(5) .select-kit` ); - let summaryElement = selectTypeElement.querySelector("summary"); + let summaryElement = selectTypeElement.querySelector( + ".select-kit-selected-name" + ); assert.equal( summaryElement.getAttribute("data-value"), getUniqueWizard.steps[i].fields[j].type, diff --git a/test/javascripts/acceptance/field-test.js b/test/javascripts/acceptance/field-test.js index b5c2e80f19..2b3d1530d5 100644 --- a/test/javascripts/acceptance/field-test.js +++ b/test/javascripts/acceptance/field-test.js @@ -14,7 +14,7 @@ import { allFieldsWizard } from "../helpers/wizard"; acceptance("Field | Fields", function (needs) { needs.pretender((server, helper) => { server.get("/w/wizard.json", () => helper.response(allFieldsWizard)); - server.get("/tags/filter/search", () => + server.get("/custom-wizard/tags/search", () => helper.response({ results: tagsJson["tags"] }) ); server.get("/u/search/users", () => helper.response(usersJson)); @@ -130,7 +130,7 @@ acceptance("Field | Fields", function (needs) { ); await fillIn(".d-modal__body.insert-link .inputs .link-url", "google.com"); await fillIn(".d-modal__body.insert-link .inputs .link-text", "Google"); - await click(".d-modal__footer button.btn-danger"); + await click(".d-modal__footer button.btn-transparent"); assert.strictEqual( query(".wizard-field.composer-field .wizard-field-composer textarea") @@ -152,10 +152,8 @@ acceptance("Field | Fields", function (needs) { test("Time", async function (assert) { await visit("/w/wizard"); - assert.ok(visible(".wizard-field.time-field .d-time-input .select-kit")); - await click( - ".wizard-field.time-field .d-time-input .select-kit .select-kit-header" - ); + assert.ok(visible(".wizard-field.time-field .select-kit")); + await click(".wizard-field.time-field .select-kit .select-kit-header"); assert.ok(visible(".wizard-field.time-field .select-kit-collection")); }); @@ -226,17 +224,64 @@ acceptance("Field | Fields", function (needs) { test("User", async function (assert) { await visit("/w/wizard"); - await fillIn( - ".wizard-field.user-selector-field .d-multi-select-trigger input", - "a" - ); + await click(".wizard-field.user-selector-field .d-multi-select-trigger"); + await fillIn(".d-multi-select__search-input", "a"); await triggerKeyEvent( - ".wizard-field.user-selector-field .d-multi-select-trigger input", + ".d-multi-select__search-input", "keyup", "a".charCodeAt(0) ); - assert.ok(visible(".wizard-field.user-selector-field .d-multi-select")); + assert.ok( + visible(".wizard-field.user-selector-field .d-multi-select-trigger") + ); // TODO: add assertion for ac results. autocomplete does not appear in time. }); }); + +acceptance("Field | Tag search request", function (needs) { + let capturedParams; + + needs.pretender((server, helper) => { + const baseTagField = allFieldsWizard.steps[0].fields.find( + (field) => field.type === "tag" + ); + const tagWizard = { + ...allFieldsWizard, + steps: [ + { + ...allFieldsWizard.steps[0], + fields: [ + { + ...baseTagField, + tag_groups: ["colours", "sizes"], + content: ["red", "blue"], + }, + ], + }, + ], + }; + + server.get("/w/wizard.json", () => helper.response(tagWizard)); + server.get("/custom-wizard/tags/search", (request) => { + capturedParams = request.queryParams; + return helper.response({ results: tagsJson["tags"] }); + }); + }); + + test("sends the field's tag groups and content allow-list to the endpoint", async function (assert) { + await visit("/w/wizard"); + await click(".wizard-field.tag-field .select-kit-header"); + + assert.strictEqual( + capturedParams.tag_groups, + "colours,sizes", + "joins the configured tag groups" + ); + assert.deepEqual( + capturedParams.content, + ["red", "blue"], + "sends the content allow-list tag names" + ); + }); +});