From bbfc5abae19e2a55fac05a31dc75558e8f894372 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Wed, 4 Dec 2019 22:13:51 +1100 Subject: [PATCH 1/9] Add rspec folder structure --- spec/components/custom_wizard/api_spec.rb | 21 +++ spec/components/custom_wizard/builder_spec.rb | 159 ++++++++++++++++++ .../custom_wizard/admin_controller_spec.rb | 5 + .../application_controller_spec.rb | 5 + .../custom_wizard/wizard_controller_spec.rb | 31 ++++ 5 files changed, 221 insertions(+) create mode 100644 spec/components/custom_wizard/api_spec.rb create mode 100644 spec/components/custom_wizard/builder_spec.rb create mode 100644 spec/requests/custom_wizard/admin_controller_spec.rb create mode 100644 spec/requests/custom_wizard/application_controller_spec.rb create mode 100644 spec/requests/custom_wizard/wizard_controller_spec.rb diff --git a/spec/components/custom_wizard/api_spec.rb b/spec/components/custom_wizard/api_spec.rb new file mode 100644 index 00000000..a7a8ba1b --- /dev/null +++ b/spec/components/custom_wizard/api_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe CustomWizard::Api do + context 'authorization' do + it 'authorizes with an oauth2 api' do + + end + + it 'refreshes the api access token' do + + end + end + + context 'endpoint' do + it 'requests an api endpoint' do + + end + end +end diff --git a/spec/components/custom_wizard/builder_spec.rb b/spec/components/custom_wizard/builder_spec.rb new file mode 100644 index 00000000..ba781cb9 --- /dev/null +++ b/spec/components/custom_wizard/builder_spec.rb @@ -0,0 +1,159 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe CustomWizard::Builder do + it "returns a wizard when enabled" do + ## implement enabled site setting first + end + + it "returns nothing when disabled" do + ## implement enabled site setting first + end + + it 'returns a wizard with prefilled data if user has partially completed' do + + end + + it 'returns a wiard with no prefilled data if options include reset' do + + end + + it 'returns nothing if the multiple submissions are disabled and user has completed' do + + end + + it 'returns nothing if the user is not permitted to see it' do + + end + + context 'building steps' do + it 'returns step data correctly' do + + end + + it 'saves permitted params' do + + end + + it 'ensures required data is present' do + + end + end + + context 'building fields' do + it 'returns field data correctly' do + + end + + it 'returns checkbox fields correctly' do + + end + + it 'returns upload fields correctly' do + + end + + it 'returns category fields correctly' do + + end + + it 'returns tag fields correctly' do + + end + + it 'returns custom dropdown fields correctly' do + + end + + it 'returns translated dropdown fields correctly' do + + end + + it 'returns preset dropdown fields correctly' do + + end + + it 'applies preset dropdown filters correctly' do + + end + + it 'prefils profile data correctly' do + + end + end + + context 'on update' do + context 'validation' do + it 'applies min length correctly' do + + end + + it 'standardises boolean entries' do + + end + + it 'requires required fields' do + ## this may require additional work? + end + + context 'submisisons' do + it 'saves submissions' do + + end + + it "doesn't save submissions if disabled" do + + end + end + end + + context 'custom_step_handlers' do + it 'runs custom step handlers' do + + end + end + + context 'actions' do + it 'runs all actions attached to a step' do + + end + + it 'interpolates wizard and user data correctly' do + + end + + it 'creates a topic' do + + end + + it 'sends a message' do + + end + + it 'doesnt sent a message if the required data is not present' do + + end + + it 'updates a profile' do + + end + + it 'calls an api' do + + end + + it 'opens a composer' do + + end + + it 'adds a user to a group' do + + end + + it 're-routes a user' do + + end + end + end +end \ No newline at end of file diff --git a/spec/requests/custom_wizard/admin_controller_spec.rb b/spec/requests/custom_wizard/admin_controller_spec.rb new file mode 100644 index 00000000..731d0de5 --- /dev/null +++ b/spec/requests/custom_wizard/admin_controller_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +describe CustomWizard::AdminController do + +end \ No newline at end of file diff --git a/spec/requests/custom_wizard/application_controller_spec.rb b/spec/requests/custom_wizard/application_controller_spec.rb new file mode 100644 index 00000000..1315748d --- /dev/null +++ b/spec/requests/custom_wizard/application_controller_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +describe ApplicationController do + +end \ No newline at end of file diff --git a/spec/requests/custom_wizard/wizard_controller_spec.rb b/spec/requests/custom_wizard/wizard_controller_spec.rb new file mode 100644 index 00000000..b457eba7 --- /dev/null +++ b/spec/requests/custom_wizard/wizard_controller_spec.rb @@ -0,0 +1,31 @@ +require 'rails_helper' + +describe CustomWizard::WizardController do + it 'returns a wizard if enabled' do + + end + + it 'returns a disabled message if disabled' do + + end + + it 'returns a missing message if no wizard exists' do + + end + + it 'returns a custom wizard theme' do + + end + + it 'updates the page title' do + + end + + it 'skips a wizard if user is allowed to skip' do + + end + + it 'returns a no skip message if user is not allowed to skip' do + + end +end \ No newline at end of file From f1fdc37a215c603a8e22a188df37e5beec75507b Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Thu, 5 Dec 2019 17:48:32 +1100 Subject: [PATCH 2/9] Add enabled setting && tests && file restructure --- .../initializers/custom-wizard-edits.js.es6 | 6 +- .../custom-wizard-redirect.js.es6 | 5 +- config/locales/server.en.yml | 1 + config/routes.rb | 36 ++ config/settings.yml | 1 + controllers/{ => custom_wizard}/admin.rb | 0 controllers/{ => custom_wizard}/api.rb | 0 controllers/{ => custom_wizard}/steps.rb | 0 controllers/{ => custom_wizard}/transfer.rb | 0 controllers/{ => custom_wizard}/wizard.rb | 0 lib/{ => custom_wizard}/api/api.rb | 0 lib/{ => custom_wizard}/api/authorization.rb | 0 lib/{ => custom_wizard}/api/endpoint.rb | 0 lib/{ => custom_wizard}/api/log_entry.rb | 0 lib/{ => custom_wizard}/builder.rb | 184 ++++++----- lib/custom_wizard/engine.rb | 6 + lib/{ => custom_wizard}/field.rb | 0 lib/{ => custom_wizard}/flags.rb | 0 lib/{ => custom_wizard}/step_updater.rb | 0 lib/{ => custom_wizard}/template.rb | 0 lib/{ => custom_wizard}/wizard.rb | 2 + lib/wizard_edits.rb | 227 ------------- plugin.rb | 310 ++++++++++++++---- .../api/authorization_serializer.rb | 0 .../api/basic_endpoint_serializer.rb | 0 .../api/endpoint_serializer.rb | 0 .../{ => custom_wizard}/api/log_serializer.rb | 0 .../{api => custom_wizard}/api_serializer.rb | 0 .../basic_api_serializer.rb | 0 spec/components/custom_wizard/builder_spec.rb | 277 +++++++++------- spec/fixtures/wizard.json | 52 +++ 31 files changed, 597 insertions(+), 510 deletions(-) create mode 100644 config/routes.rb rename controllers/{ => custom_wizard}/admin.rb (100%) rename controllers/{ => custom_wizard}/api.rb (100%) rename controllers/{ => custom_wizard}/steps.rb (100%) rename controllers/{ => custom_wizard}/transfer.rb (100%) rename controllers/{ => custom_wizard}/wizard.rb (100%) rename lib/{ => custom_wizard}/api/api.rb (100%) rename lib/{ => custom_wizard}/api/authorization.rb (100%) rename lib/{ => custom_wizard}/api/endpoint.rb (100%) rename lib/{ => custom_wizard}/api/log_entry.rb (100%) rename lib/{ => custom_wizard}/builder.rb (80%) create mode 100644 lib/custom_wizard/engine.rb rename lib/{ => custom_wizard}/field.rb (100%) rename lib/{ => custom_wizard}/flags.rb (100%) rename lib/{ => custom_wizard}/step_updater.rb (100%) rename lib/{ => custom_wizard}/template.rb (100%) rename lib/{ => custom_wizard}/wizard.rb (99%) delete mode 100644 lib/wizard_edits.rb rename serializers/{ => custom_wizard}/api/authorization_serializer.rb (100%) rename serializers/{ => custom_wizard}/api/basic_endpoint_serializer.rb (100%) rename serializers/{ => custom_wizard}/api/endpoint_serializer.rb (100%) rename serializers/{ => custom_wizard}/api/log_serializer.rb (100%) rename serializers/{api => custom_wizard}/api_serializer.rb (100%) rename serializers/{api => custom_wizard}/basic_api_serializer.rb (100%) create mode 100644 spec/fixtures/wizard.json diff --git a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 index 172f0f34..1e4436cb 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-edits.js.es6 @@ -3,7 +3,11 @@ import DiscourseURL from 'discourse/lib/url'; export default { name: 'custom-wizard-edits', - initialize() { + initialize(container) { + const siteSettings = container.lookup('site-settings:main'); + + if (!siteSettings.custom_wizard_enabled) return; + withPluginApi('0.8.12', api => { api.modifyClass('component:global-notice', { buildBuffer(buffer) { diff --git a/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6 b/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6 index 4f60b57a..32827b9b 100644 --- a/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6 +++ b/assets/javascripts/discourse/initializers/custom-wizard-redirect.js.es6 @@ -6,8 +6,9 @@ export default { initialize: function (container) { const messageBus = container.lookup('message-bus:main'); - - if (!messageBus) { return; } + const siteSettings = container.lookup('site-settings:main'); + + if (!siteSettings.custom_wizard_enabled || !messageBus) return; messageBus.subscribe("/redirect_to_wizard", function (wizardId) { const wizardUrl = window.location.origin + '/w/' + wizardId; diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index dd36b72f..e8e4a7c1 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -21,6 +21,7 @@ en: no_valid_wizards: "File doesn't contain any valid wizards" site_settings: + custom_wizard_enabled: "Enable custom wizards." wizard_redirect_exclude_paths: "Routes excluded from wizard redirects." wizard_recognised_image_upload_formats: "File types which will result in upload displaying an image preview" wizard_national_flags_label_cue: "The characters that must appear in the dropdown field label for it to show a flag" \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 00000000..764c4e42 --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,36 @@ +CustomWizard::Engine.routes.draw do + get ':wizard_id' => 'wizard#index' + put ':wizard_id/skip' => 'wizard#skip' + get ':wizard_id/steps' => 'wizard#index' + get ':wizard_id/steps/:step_id' => 'wizard#index' + put ':wizard_id/steps/:step_id' => 'steps#update' +end + +Discourse::Application.routes.append do + mount ::CustomWizard::Engine, at: 'w' + post 'wizard/authorization/callback' => "custom_wizard/authorization#callback" + + scope module: 'custom_wizard', constraints: AdminConstraint.new do + get 'admin/wizards' => 'admin#index' + get 'admin/wizards/field-types' => 'admin#field_types' + get 'admin/wizards/custom' => 'admin#index' + get 'admin/wizards/custom/new' => 'admin#index' + get 'admin/wizards/custom/all' => 'admin#custom_wizards' + get 'admin/wizards/custom/:wizard_id' => 'admin#find_wizard' + put 'admin/wizards/custom/save' => 'admin#save' + delete 'admin/wizards/custom/remove' => 'admin#remove' + get 'admin/wizards/submissions' => 'admin#index' + get 'admin/wizards/submissions/:wizard_id' => 'admin#submissions' + get 'admin/wizards/apis' => 'api#list' + get 'admin/wizards/apis/new' => 'api#index' + get 'admin/wizards/apis/:name' => 'api#find' + put 'admin/wizards/apis/:name' => 'api#save' + delete 'admin/wizards/apis/:name' => 'api#remove' + delete 'admin/wizards/apis/logs/:name' => 'api#clearlogs' + get 'admin/wizards/apis/:name/redirect' => 'api#redirect' + get 'admin/wizards/apis/:name/authorize' => 'api#authorize' + get 'admin/wizards/transfer' => 'transfer#index' + get 'admin/wizards/transfer/export' => 'transfer#export' + post 'admin/wizards/transfer/import' => 'transfer#import' + end +end \ No newline at end of file diff --git a/config/settings.yml b/config/settings.yml index e99f28bd..7c51b15f 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -1,4 +1,5 @@ plugins: + custom_wizard_enabled: true wizard_redirect_exclude_paths: client: true type: list diff --git a/controllers/admin.rb b/controllers/custom_wizard/admin.rb similarity index 100% rename from controllers/admin.rb rename to controllers/custom_wizard/admin.rb diff --git a/controllers/api.rb b/controllers/custom_wizard/api.rb similarity index 100% rename from controllers/api.rb rename to controllers/custom_wizard/api.rb diff --git a/controllers/steps.rb b/controllers/custom_wizard/steps.rb similarity index 100% rename from controllers/steps.rb rename to controllers/custom_wizard/steps.rb diff --git a/controllers/transfer.rb b/controllers/custom_wizard/transfer.rb similarity index 100% rename from controllers/transfer.rb rename to controllers/custom_wizard/transfer.rb diff --git a/controllers/wizard.rb b/controllers/custom_wizard/wizard.rb similarity index 100% rename from controllers/wizard.rb rename to controllers/custom_wizard/wizard.rb diff --git a/lib/api/api.rb b/lib/custom_wizard/api/api.rb similarity index 100% rename from lib/api/api.rb rename to lib/custom_wizard/api/api.rb diff --git a/lib/api/authorization.rb b/lib/custom_wizard/api/authorization.rb similarity index 100% rename from lib/api/authorization.rb rename to lib/custom_wizard/api/authorization.rb diff --git a/lib/api/endpoint.rb b/lib/custom_wizard/api/endpoint.rb similarity index 100% rename from lib/api/endpoint.rb rename to lib/custom_wizard/api/endpoint.rb diff --git a/lib/api/log_entry.rb b/lib/custom_wizard/api/log_entry.rb similarity index 100% rename from lib/api/log_entry.rb rename to lib/custom_wizard/api/log_entry.rb diff --git a/lib/builder.rb b/lib/custom_wizard/builder.rb similarity index 80% rename from lib/builder.rb rename to lib/custom_wizard/builder.rb index b51b6e00..abdc4cd1 100644 --- a/lib/builder.rb +++ b/lib/custom_wizard/builder.rb @@ -77,112 +77,118 @@ class CustomWizard::Builder end def build(build_opts = {}, params = {}) - unless (@wizard.completed? && !@wizard.multiple_submissions && !@wizard.user.admin) || !@steps || !@wizard.permitted? - reset_submissions if build_opts[:reset] + + return @wizard if !SiteSetting.custom_wizard_enabled || + (!@wizard.multiple_submissions && + @wizard.completed? && + !@wizard.user.admin) || + !@steps || + !@wizard.permitted? + + reset_submissions if build_opts[:reset] - @steps.each do |step_template| - @wizard.append_step(step_template['id']) do |step| - step.title = step_template['title'] if step_template['title'] - step.description = step_template['description'] if step_template['description'] - step.banner = step_template['banner'] if step_template['banner'] - step.key = step_template['key'] if step_template['key'] - step.permitted = true + @steps.each do |step_template| + @wizard.append_step(step_template['id']) do |step| + step.title = step_template['title'] if step_template['title'] + step.description = step_template['description'] if step_template['description'] + step.banner = step_template['banner'] if step_template['banner'] + step.key = step_template['key'] if step_template['key'] + step.permitted = true - if permitted_params = step_template['permitted_params'] - permitted_data = {} + if permitted_params = step_template['permitted_params'] + permitted_data = {} - permitted_params.each do |param| - key = param['key'].to_sym - permitted_data[key] = params[key] if params[key] - end - - if permitted_data.present? - current_data = @submissions.last || {} - save_submissions(current_data.merge(permitted_data), false) - end + permitted_params.each do |param| + key = param['key'].to_sym + permitted_data[key] = params[key] if params[key] end - if required_data = step_template['required_data'] - if !@submissions.last && required_data.present? - step.permitted = false - next - end + if permitted_data.present? + current_data = @submissions.last || {} + save_submissions(current_data.merge(permitted_data), false) + end + end - required_data.each do |rd| - if rd['connector'] === 'equals' - step.permitted = @submissions.last[rd['key']] == @submissions.last[rd['value']] - end - end - - if !step.permitted - step.permitted_message = step_template['required_data_message'] if step_template['required_data_message'] - next - end + if required_data = step_template['required_data'] + if !@submissions.last && required_data.present? + step.permitted = false + next end + required_data.each do |rd| + if rd['connector'] === 'equals' + step.permitted = @submissions.last[rd['key']] == @submissions.last[rd['value']] + end + end + + if !step.permitted + step.permitted_message = step_template['required_data_message'] if step_template['required_data_message'] + next + end + end + + if step_template['fields'] && step_template['fields'].length + step_template['fields'].each do |field_template| + append_field(step, step_template, field_template, build_opts) + end + end + + step.on_update do |updater| + @updater = updater + user = @wizard.user + if step_template['fields'] && step_template['fields'].length - step_template['fields'].each do |field_template| - append_field(step, step_template, field_template, build_opts) + step_template['fields'].each do |field| + validate_field(field, updater, step_template) if field['type'] != 'text-only' + end + end + + next if updater.errors.any? + + CustomWizard::Builder.step_handlers.each do |handler| + if handler[:wizard_id] == @wizard.id + handler[:block].call(self) end end - step.on_update do |updater| - @updater = updater - user = @wizard.user - - if step_template['fields'] && step_template['fields'].length - step_template['fields'].each do |field| - validate_field(field, updater, step_template) if field['type'] != 'text-only' - end + next if updater.errors.any? + + data = updater.fields.to_h + + ## if the wizard has data from the previous steps make that accessible to the actions. + if @submissions && @submissions.last && !@submissions.last.key?("submitted_at") + submission = @submissions.last + data = submission.merge(data) + end + + if step_template['actions'] && step_template['actions'].length && data + step_template['actions'].each do |action| + self.send(action['type'].to_sym, user, action, data) end - - next if updater.errors.any? + end - CustomWizard::Builder.step_handlers.each do |handler| - if handler[:wizard_id] == @wizard.id - handler[:block].call(self) - end - end + final_step = updater.step.next.nil? + + if route_to = data['route_to'] + data.delete('route_to') + end - next if updater.errors.any? + if @wizard.save_submissions && updater.errors.empty? + save_submissions(data, final_step) + elsif final_step + PluginStore.remove("#{@wizard.id}_submissions", @wizard.user.id) + end - data = updater.fields.to_h + if final_step && @wizard.id === @wizard.user.custom_fields['redirect_to_wizard'] + @wizard.user.custom_fields.delete('redirect_to_wizard'); + @wizard.user.save_custom_fields(true) + end - ## if the wizard has data from the previous steps make that accessible to the actions. - if @submissions && @submissions.last && !@submissions.last.key?("submitted_at") - submission = @submissions.last - data = submission.merge(data) - end - - if step_template['actions'] && step_template['actions'].length && data - step_template['actions'].each do |action| - self.send(action['type'].to_sym, user, action, data) - end - end - - final_step = updater.step.next.nil? - - if route_to = data['route_to'] - data.delete('route_to') - end - - if @wizard.save_submissions && updater.errors.empty? - save_submissions(data, final_step) - elsif final_step - PluginStore.remove("#{@wizard.id}_submissions", @wizard.user.id) - end - - if final_step && @wizard.id === @wizard.user.custom_fields['redirect_to_wizard'] - @wizard.user.custom_fields.delete('redirect_to_wizard'); - @wizard.user.save_custom_fields(true) - end - - if updater.errors.empty? - if final_step - updater.result[:redirect_on_complete] = route_to || data['redirect_on_complete'] - elsif route_to - updater.result[:redirect_on_next] = route_to - end + if updater.errors.empty? + if final_step + updater.result[:redirect_on_complete] = route_to || data['redirect_on_complete'] + elsif route_to + updater.result[:redirect_on_next] = route_to end end end diff --git a/lib/custom_wizard/engine.rb b/lib/custom_wizard/engine.rb new file mode 100644 index 00000000..80681bee --- /dev/null +++ b/lib/custom_wizard/engine.rb @@ -0,0 +1,6 @@ +module ::CustomWizard + class Engine < ::Rails::Engine + engine_name 'custom_wizard' + isolate_namespace CustomWizard + end +end \ No newline at end of file diff --git a/lib/field.rb b/lib/custom_wizard/field.rb similarity index 100% rename from lib/field.rb rename to lib/custom_wizard/field.rb diff --git a/lib/flags.rb b/lib/custom_wizard/flags.rb similarity index 100% rename from lib/flags.rb rename to lib/custom_wizard/flags.rb diff --git a/lib/step_updater.rb b/lib/custom_wizard/step_updater.rb similarity index 100% rename from lib/step_updater.rb rename to lib/custom_wizard/step_updater.rb diff --git a/lib/template.rb b/lib/custom_wizard/template.rb similarity index 100% rename from lib/template.rb rename to lib/custom_wizard/template.rb diff --git a/lib/wizard.rb b/lib/custom_wizard/wizard.rb similarity index 99% rename from lib/wizard.rb rename to lib/custom_wizard/wizard.rb index 8fe8a690..b8d0f7af 100644 --- a/lib/wizard.rb +++ b/lib/custom_wizard/wizard.rb @@ -3,6 +3,8 @@ require_dependency 'wizard/field' require_dependency 'wizard/step_updater' require_dependency 'wizard/builder' +UserHistory.actions[:custom_wizard_step] = 1000 + class CustomWizard::Wizard attr_reader :steps, :user diff --git a/lib/wizard_edits.rb b/lib/wizard_edits.rb deleted file mode 100644 index 9c834808..00000000 --- a/lib/wizard_edits.rb +++ /dev/null @@ -1,227 +0,0 @@ -require_dependency 'wizard' -require_dependency 'wizard/field' -require_dependency 'wizard/step' - -::Wizard.class_eval do - def self.user_requires_completion?(user) - wizard_result = self.new(user).requires_completion? - return wizard_result if wizard_result - - custom_redirect = false - - if user && user.first_seen_at.blank? && wizard_id = CustomWizard::Wizard.after_signup - wizard = CustomWizard::Wizard.create(user, wizard_id) - - if !wizard.completed? && wizard.permitted? - custom_redirect = true - CustomWizard::Wizard.set_wizard_redirect(user, wizard_id) - end - end - - !!custom_redirect - end -end - -::Wizard::Field.class_eval do - attr_reader :label, :description, :image, :key, :min_length, :file_types, :limit, :property - attr_accessor :dropdown_none - - def initialize(attrs) - @attrs = attrs || {} - @id = attrs[:id] - @type = attrs[:type] - @required = !!attrs[:required] - @description = attrs[:description] - @image = attrs[:image] - @key = attrs[:key] - @min_length = attrs[:min_length] - @value = attrs[:value] - @choices = [] - @dropdown_none = attrs[:dropdown_none] - @file_types = attrs[:file_types] - @limit = attrs[:limit] - @property = attrs[:property] - end - - def label - @label ||= PrettyText.cook(@attrs[:label]) - end -end - -::Wizard::Choice.class_eval do - def initialize(id, opts) - @id = id - @opts = opts - @data = opts[:data] - @extra_label = opts[:extra_label] - @icon = opts[:icon] - end - - def label - @label ||= PrettyText.cook(@opts[:label]) - end -end - -class ::Wizard::Step - attr_accessor :title, :description, :key, :permitted, :permitted_message -end - -::WizardSerializer.class_eval do - attributes :id, - :name, - :background, - :completed, - :required, - :min_trust, - :permitted, - :user, - :categories, - :uncategorized_category_id - - def id - object.id - end - - def include_id? - object.respond_to?(:id) - end - - def name - object.name - end - - def include_name? - object.respond_to?(:name) - end - - def background - object.background - end - - def include_background? - object.respond_to?(:background) - end - - def completed - object.completed? - end - - def include_completed? - object.completed? && - (!object.respond_to?(:multiple_submissions) || !object.multiple_submissions) && - !scope.is_admin? - end - - def min_trust - object.min_trust - end - - def include_min_trust? - object.respond_to?(:min_trust) - end - - def permitted - object.permitted? - end - - def include_permitted? - object.respond_to?(:permitted?) - end - - def include_start? - object.start && include_steps? - end - - def include_steps? - !include_completed? - end - - def required - object.required - end - - def include_required? - object.respond_to?(:required) - end - - def user - object.user - end - - def categories - begin - site = ::Site.new(scope) - ::ActiveModel::ArraySerializer.new(site.categories, each_serializer: BasicCategorySerializer) - rescue => e - puts "HERE IS THE ERROR: #{e.inspect}" - end - end - - def uncategorized_category_id - SiteSetting.uncategorized_category_id - end -end - -::WizardStepSerializer.class_eval do - attributes :permitted, :permitted_message - - def title - return PrettyText.cook(object.title) if object.title - PrettyText.cook(I18n.t("#{object.key || i18n_key}.title", default: '')) - 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 permitted - object.permitted - end - - def permitted_message - object.permitted_message - end -end - -::WizardFieldSerializer.class_eval do - attributes :dropdown_none, :image, :file_types, :limit, :property - - def label - return object.label if object.label.present? - I18n.t("#{object.key || i18n_key}.label", default: '') - 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 image - object.image - end - - def include_image? - object.image.present? - end - - def placeholder - I18n.t("#{object.key || i18n_key}.placeholder", default: '') - end - - def dropdown_none - object.dropdown_none - end - - def file_types - object.file_types - end - - def limit - object.limit - end - - def property - object.property - end -end diff --git a/plugin.rb b/plugin.rb index 659720b5..a02013e0 100644 --- a/plugin.rb +++ b/plugin.rb @@ -8,11 +8,12 @@ register_asset 'stylesheets/wizard_custom_admin.scss' register_asset 'lib/jquery.timepicker.min.js' register_asset 'lib/jquery.timepicker.scss' +enabled_site_setting :custom_wizard_enabled + config = Rails.application.config config.assets.paths << Rails.root.join('plugins', 'discourse-custom-wizard', 'assets', 'javascripts') config.assets.paths << Rails.root.join('plugins', 'discourse-custom-wizard', 'assets', 'stylesheets', 'wizard') - if Rails.env.production? config.assets.precompile += %w{ wizard-custom-guest.js @@ -35,79 +36,260 @@ if respond_to?(:register_svg_icon) end after_initialize do - UserHistory.actions[:custom_wizard_step] = 1000 + [ + '../lib/custom_wizard/engine.rb', + '../config/routes.rb', + '../controllers/custom_wizard/wizard.rb', + '../controllers/custom_wizard/steps.rb', + '../controllers/custom_wizard/admin.rb', + '../controllers/custom_wizard/transfer.rb', + '../controllers/custom_wizard/api.rb', + '../jobs/clear_after_time_wizard.rb', + '../jobs/refresh_api_access_token.rb', + '../jobs/set_after_time_wizard.rb', + '../lib/custom_wizard/builder.rb', + '../lib/custom_wizard/field.rb', + '../lib/custom_wizard/flags.rb', + '../lib/custom_wizard/step_updater.rb', + '../lib/custom_wizard/template.rb', + '../lib/custom_wizard/wizard.rb', + '../lib/custom_wizard/api/api.rb', + '../lib/custom_wizard/api/authorization.rb', + '../lib/custom_wizard/api/endpoint.rb', + '../lib/custom_wizard/api/log_entry.rb', + '../serializers/custom_wizard/api_serializer.rb', + '../serializers/custom_wizard/basic_api_serializer.rb', + '../serializers/custom_wizard/api/authorization_serializer.rb', + '../serializers/custom_wizard/api/basic_endpoint_serializer.rb', + '../serializers/custom_wizard/api/endpoint_serializer.rb', + '../serializers/custom_wizard/api/log_serializer.rb' + ].each do |path| + load File.expand_path(path, __FILE__) + end + + ::Wizard.class_eval do + def self.user_requires_completion?(user) + wizard_result = self.new(user).requires_completion? + return wizard_result if wizard_result - module ::CustomWizard - class Engine < ::Rails::Engine - engine_name 'custom_wizard' - isolate_namespace CustomWizard + custom_redirect = false + + if user && user.first_seen_at.blank? && wizard_id = CustomWizard::Wizard.after_signup + wizard = CustomWizard::Wizard.create(user, wizard_id) + + if !wizard.completed? && wizard.permitted? + custom_redirect = true + CustomWizard::Wizard.set_wizard_redirect(user, wizard_id) + end + end + + !!custom_redirect end end - CustomWizard::Engine.routes.draw do - get ':wizard_id' => 'wizard#index' - put ':wizard_id/skip' => 'wizard#skip' - get ':wizard_id/steps' => 'wizard#index' - get ':wizard_id/steps/:step_id' => 'wizard#index' - put ':wizard_id/steps/:step_id' => 'steps#update' - end + ::Wizard::Field.class_eval do + attr_reader :label, :description, :image, :key, :min_length, :file_types, :limit, :property + attr_accessor :dropdown_none - Discourse::Application.routes.append do - mount ::CustomWizard::Engine, at: 'w' - post 'wizard/authorization/callback' => "custom_wizard/authorization#callback" + def initialize(attrs) + @attrs = attrs || {} + @id = attrs[:id] + @type = attrs[:type] + @required = !!attrs[:required] + @description = attrs[:description] + @image = attrs[:image] + @key = attrs[:key] + @min_length = attrs[:min_length] + @value = attrs[:value] + @choices = [] + @dropdown_none = attrs[:dropdown_none] + @file_types = attrs[:file_types] + @limit = attrs[:limit] + @property = attrs[:property] + end - scope module: 'custom_wizard', constraints: AdminConstraint.new do - get 'admin/wizards' => 'admin#index' - get 'admin/wizards/field-types' => 'admin#field_types' - get 'admin/wizards/custom' => 'admin#index' - get 'admin/wizards/custom/new' => 'admin#index' - get 'admin/wizards/custom/all' => 'admin#custom_wizards' - get 'admin/wizards/custom/:wizard_id' => 'admin#find_wizard' - put 'admin/wizards/custom/save' => 'admin#save' - delete 'admin/wizards/custom/remove' => 'admin#remove' - get 'admin/wizards/submissions' => 'admin#index' - get 'admin/wizards/submissions/:wizard_id' => 'admin#submissions' - get 'admin/wizards/apis' => 'api#list' - get 'admin/wizards/apis/new' => 'api#index' - get 'admin/wizards/apis/:name' => 'api#find' - put 'admin/wizards/apis/:name' => 'api#save' - delete 'admin/wizards/apis/:name' => 'api#remove' - delete 'admin/wizards/apis/logs/:name' => 'api#clearlogs' - get 'admin/wizards/apis/:name/redirect' => 'api#redirect' - get 'admin/wizards/apis/:name/authorize' => 'api#authorize' - get 'admin/wizards/transfer' => 'transfer#index' - get 'admin/wizards/transfer/export' => 'transfer#export' - post 'admin/wizards/transfer/import' => 'transfer#import' + def label + @label ||= PrettyText.cook(@attrs[:label]) end end - load File.expand_path('../jobs/clear_after_time_wizard.rb', __FILE__) - load File.expand_path('../jobs/set_after_time_wizard.rb', __FILE__) - load File.expand_path('../lib/builder.rb', __FILE__) - load File.expand_path('../lib/field.rb', __FILE__) - load File.expand_path('../lib/flags.rb', __FILE__) - load File.expand_path('../lib/step_updater.rb', __FILE__) - load File.expand_path('../lib/template.rb', __FILE__) - load File.expand_path('../lib/wizard.rb', __FILE__) - load File.expand_path('../lib/wizard_edits.rb', __FILE__) - load File.expand_path('../controllers/wizard.rb', __FILE__) - load File.expand_path('../controllers/steps.rb', __FILE__) - load File.expand_path('../controllers/admin.rb', __FILE__) - #transfer code - load File.expand_path('../controllers/transfer.rb', __FILE__) + ::Wizard::Choice.class_eval do + def initialize(id, opts) + @id = id + @opts = opts + @data = opts[:data] + @extra_label = opts[:extra_label] + @icon = opts[:icon] + end - load File.expand_path('../jobs/refresh_api_access_token.rb', __FILE__) - load File.expand_path('../lib/api/api.rb', __FILE__) - load File.expand_path('../lib/api/authorization.rb', __FILE__) - load File.expand_path('../lib/api/endpoint.rb', __FILE__) - load File.expand_path('../lib/api/log_entry.rb', __FILE__) - load File.expand_path('../controllers/api.rb', __FILE__) - load File.expand_path('../serializers/api/api_serializer.rb', __FILE__) - load File.expand_path('../serializers/api/authorization_serializer.rb', __FILE__) - load File.expand_path('../serializers/api/basic_api_serializer.rb', __FILE__) - load File.expand_path('../serializers/api/endpoint_serializer.rb', __FILE__) - load File.expand_path('../serializers/api/basic_endpoint_serializer.rb', __FILE__) - load File.expand_path('../serializers/api/log_serializer.rb', __FILE__) + def label + @label ||= PrettyText.cook(@opts[:label]) + end + end + + class ::Wizard::Step + attr_accessor :title, :description, :key, :permitted, :permitted_message + end + + ::WizardSerializer.class_eval do + attributes :id, + :name, + :background, + :completed, + :required, + :min_trust, + :permitted, + :user, + :categories, + :uncategorized_category_id + + def id + object.id + end + + def include_id? + object.respond_to?(:id) + end + + def name + object.name + end + + def include_name? + object.respond_to?(:name) + end + + def background + object.background + end + + def include_background? + object.respond_to?(:background) + end + + def completed + object.completed? + end + + def include_completed? + object.completed? && + (!object.respond_to?(:multiple_submissions) || !object.multiple_submissions) && + !scope.is_admin? + end + + def min_trust + object.min_trust + end + + def include_min_trust? + object.respond_to?(:min_trust) + end + + def permitted + object.permitted? + end + + def include_permitted? + object.respond_to?(:permitted?) + end + + def include_start? + object.start && include_steps? + end + + def include_steps? + !include_completed? + end + + def required + object.required + end + + def include_required? + object.respond_to?(:required) + end + + def user + object.user + end + + def categories + begin + site = ::Site.new(scope) + ::ActiveModel::ArraySerializer.new(site.categories, each_serializer: BasicCategorySerializer) + rescue => e + puts "HERE IS THE ERROR: #{e.inspect}" + end + end + + def uncategorized_category_id + SiteSetting.uncategorized_category_id + end + end + + ::WizardStepSerializer.class_eval do + attributes :permitted, :permitted_message + + def title + return PrettyText.cook(object.title) if object.title + PrettyText.cook(I18n.t("#{object.key || i18n_key}.title", default: '')) + 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 permitted + object.permitted + end + + def permitted_message + object.permitted_message + end + end + + ::WizardFieldSerializer.class_eval do + attributes :dropdown_none, :image, :file_types, :limit, :property + + def label + return object.label if object.label.present? + I18n.t("#{object.key || i18n_key}.label", default: '') + 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 image + object.image + end + + def include_image? + object.image.present? + end + + def placeholder + I18n.t("#{object.key || i18n_key}.placeholder", default: '') + end + + def dropdown_none + object.dropdown_none + end + + def file_types + object.file_types + end + + def limit + object.limit + end + + def property + object.property + end + end ::UsersController.class_eval do def wizard_path diff --git a/serializers/api/authorization_serializer.rb b/serializers/custom_wizard/api/authorization_serializer.rb similarity index 100% rename from serializers/api/authorization_serializer.rb rename to serializers/custom_wizard/api/authorization_serializer.rb diff --git a/serializers/api/basic_endpoint_serializer.rb b/serializers/custom_wizard/api/basic_endpoint_serializer.rb similarity index 100% rename from serializers/api/basic_endpoint_serializer.rb rename to serializers/custom_wizard/api/basic_endpoint_serializer.rb diff --git a/serializers/api/endpoint_serializer.rb b/serializers/custom_wizard/api/endpoint_serializer.rb similarity index 100% rename from serializers/api/endpoint_serializer.rb rename to serializers/custom_wizard/api/endpoint_serializer.rb diff --git a/serializers/api/log_serializer.rb b/serializers/custom_wizard/api/log_serializer.rb similarity index 100% rename from serializers/api/log_serializer.rb rename to serializers/custom_wizard/api/log_serializer.rb diff --git a/serializers/api/api_serializer.rb b/serializers/custom_wizard/api_serializer.rb similarity index 100% rename from serializers/api/api_serializer.rb rename to serializers/custom_wizard/api_serializer.rb diff --git a/serializers/api/basic_api_serializer.rb b/serializers/custom_wizard/basic_api_serializer.rb similarity index 100% rename from serializers/api/basic_api_serializer.rb rename to serializers/custom_wizard/basic_api_serializer.rb diff --git a/spec/components/custom_wizard/builder_spec.rb b/spec/components/custom_wizard/builder_spec.rb index ba781cb9..5d0a8c73 100644 --- a/spec/components/custom_wizard/builder_spec.rb +++ b/spec/components/custom_wizard/builder_spec.rb @@ -3,156 +3,179 @@ require 'rails_helper' describe CustomWizard::Builder do - it "returns a wizard when enabled" do - ## implement enabled site setting first + fab!(:user) { Fabricate(:user) } + + before do + @template = File.open( + "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" + ).read end - it "returns nothing when disabled" do - ## implement enabled site setting first + it "returns no steps when disabled" do + CustomWizard::Wizard.add_wizard(@template) + SiteSetting.custom_wizard_enabled = false + wizard = CustomWizard::Builder.new(user, 'welcome').build + expect(wizard.steps.length).to eq(0) + expect(wizard.name).to eq('Welcome') end - it 'returns a wizard with prefilled data if user has partially completed' do - - end - - it 'returns a wiard with no prefilled data if options include reset' do - - end - - it 'returns nothing if the multiple submissions are disabled and user has completed' do + context 'enabled' do + before do + SiteSetting.custom_wizard_enabled = true + end - end - - it 'returns nothing if the user is not permitted to see it' do - - end - - context 'building steps' do - it 'returns step data correctly' do + it "returns steps when enabled" do + CustomWizard::Wizard.add_wizard(@template) + wizard = CustomWizard::Builder.new(user, 'welcome').build + expect(wizard.steps.length).to eq(2) + end + + it 'returns no steps if the multiple submissions are disabled and user has completed' do + @template['multiple_submissions'] = false + CustomWizard::Wizard.add_wizard(@template) + wizard = CustomWizard::Builder.new(user, 'welcome').build + expect(wizard.steps.length).to eq(0) + end + + it 'returns nothing if the user is not permitted to see it' do + + end + + it 'returns a wizard with prefilled data if user has partially completed' do + + end + + it 'returns a wizard with no prefilled data if options include reset' do + + end + + context 'building steps' do + it 'returns step data correctly' do + + end - end - - it 'saves permitted params' do - - end - - it 'ensures required data is present' do - - end - end - - context 'building fields' do - it 'returns field data correctly' do - - end - - it 'returns checkbox fields correctly' do - - end - - it 'returns upload fields correctly' do - - end - - it 'returns category fields correctly' do - - end - - it 'returns tag fields correctly' do - - end - - it 'returns custom dropdown fields correctly' do - - end - - it 'returns translated dropdown fields correctly' do - - end - - it 'returns preset dropdown fields correctly' do - - end - - it 'applies preset dropdown filters correctly' do - - end - - it 'prefils profile data correctly' do - - end - end - - context 'on update' do - context 'validation' do - it 'applies min length correctly' do + it 'saves permitted params' do end - it 'standardises boolean entries' do + it 'ensures required data is present' do + + end + end + + context 'building fields' do + it 'returns field data correctly' do end - it 'requires required fields' do - ## this may require additional work? + it 'returns checkbox fields correctly' do + end - context 'submisisons' do - it 'saves submissions' do + it 'returns upload fields correctly' do + + end + + it 'returns category fields correctly' do + + end + + it 'returns tag fields correctly' do + + end + + it 'returns custom dropdown fields correctly' do + + end + + it 'returns translated dropdown fields correctly' do + + end + + it 'returns preset dropdown fields correctly' do + + end + + it 'applies preset dropdown filters correctly' do + + end + + it 'prefils profile data correctly' do + + end + end + + context 'on update' do + context 'validation' do + it 'applies min length correctly' do end - it "doesn't save submissions if disabled" do + it 'standardises boolean entries' do + + end + + it 'requires required fields' do + ## this may require additional work? + end + + context 'submisisons' do + it 'saves submissions' do + + end + + it "doesn't save submissions if disabled" do + + end + end + end + + context 'custom_step_handlers' do + it 'runs custom step handlers' do end end - end - - context 'custom_step_handlers' do - it 'runs custom step handlers' do - end - end - - context 'actions' do - it 'runs all actions attached to a step' do + context 'actions' do + it 'runs all actions attached to a step' do + + end - end - - it 'interpolates wizard and user data correctly' do - - end - - it 'creates a topic' do - - end - - it 'sends a message' do - - end - - it 'doesnt sent a message if the required data is not present' do - - end - - it 'updates a profile' do - - end - - it 'calls an api' do - - end - - it 'opens a composer' do - - end - - it 'adds a user to a group' do - - end - - it 're-routes a user' do + it 'interpolates wizard and user data correctly' do + end + + it 'creates a topic' do + + end + + it 'sends a message' do + + end + + it 'doesnt sent a message if the required data is not present' do + + end + + it 'updates a profile' do + + end + + it 'calls an api' do + + end + + it 'opens a composer' do + + end + + it 'adds a user to a group' do + + end + + it 're-routes a user' do + + end end end end diff --git a/spec/fixtures/wizard.json b/spec/fixtures/wizard.json new file mode 100644 index 00000000..e384427b --- /dev/null +++ b/spec/fixtures/wizard.json @@ -0,0 +1,52 @@ +{ + "id": "welcome", + "name": "Welcome", + "background": "#006da3", + "save_submissions": true, + "multiple_submissions": true, + "after_signup": true, + "theme_id": 4, + "steps": [ + { + "id": "step_1", + "title": "Welcome to Pavilion", + "raw_description": "Hey there, thanks for signing up.\n\nWe're Pavilion, an international freelancer cooperative that specialises in online communities.\n\nThis site is our own community, where we work with our clients, users of our open source work and our broader community.\n\n", + "description": "

Hey there, thanks for signing up.

\n

We’re Pavilion, an international freelancer cooperative that specialises in online communities.

\n

This site is our own community, where we work with our clients, users of our open source work and our broader community.

" + }, + { + "id": "step_2", + "title": "Tell us about you", + "raw_description": "We'd like to know a little more about you. Add a your name and your website below. This will update your user profile.", + "fields": [ + { + "id": "name", + "type": "text", + "label": "Name", + "required": true + }, + { + "id": "field_3", + "label": "Website", + "type": "text" + } + ], + "actions": [ + { + "id": "update_profile", + "type": "update_profile", + "profile_updates": [ + { + "key": "name", + "value": "name" + }, + { + "key": "field_3", + "value": "website" + } + ] + } + ], + "description": "

We’d like to know a little more about you. Add a your name and your website below. This will update your user profile.

" + } + ] +} \ No newline at end of file From ba33576f51f79b28348f752e220c23931f0ac7ac Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Thu, 5 Dec 2019 19:05:21 +1100 Subject: [PATCH 3/9] File restructuring --- controllers/application_controller.rb | 27 ++ controllers/extra_locales_controller.rb | 20 ++ controllers/invites_controller.rb | 22 ++ lib/wizard/choice.rb | 17 ++ lib/wizard/field.rb | 37 +++ lib/wizard/step.rb | 7 + plugin.rb | 353 +++--------------------- serializers/site_serializer.rb | 22 ++ serializers/wizard_field_serializer.rb | 45 +++ serializers/wizard_serializer.rb | 99 +++++++ serializers/wizard_step_serializer.rb | 25 ++ 11 files changed, 359 insertions(+), 315 deletions(-) create mode 100644 controllers/application_controller.rb create mode 100644 controllers/extra_locales_controller.rb create mode 100644 controllers/invites_controller.rb create mode 100644 lib/wizard/choice.rb create mode 100644 lib/wizard/field.rb create mode 100644 lib/wizard/step.rb create mode 100644 serializers/site_serializer.rb create mode 100644 serializers/wizard_field_serializer.rb create mode 100644 serializers/wizard_serializer.rb create mode 100644 serializers/wizard_step_serializer.rb diff --git a/controllers/application_controller.rb b/controllers/application_controller.rb new file mode 100644 index 00000000..5e6358d2 --- /dev/null +++ b/controllers/application_controller.rb @@ -0,0 +1,27 @@ +module ApplicationControllerCWExtension + extend ActiveSupport::Concern + + included do + before_action :redirect_to_wizard_if_required, if: :current_user + end + + def redirect_to_wizard_if_required + wizard_id = current_user.custom_fields['redirect_to_wizard'] + @excluded_routes ||= SiteSetting.wizard_redirect_exclude_paths.split('|') + ['/w/'] + url = request.referer || request.original_url + + if request.format === 'text/html' && !@excluded_routes.any? {|str| /#{str}/ =~ url} && wizard_id + if request.referer !~ /\/w\// && request.referer !~ /\/invites\// + CustomWizard::Wizard.set_submission_redirect(current_user, wizard_id, request.referer) + end + + if CustomWizard::Wizard.exists?(wizard_id) + redirect_to "/w/#{wizard_id.dasherize}" + end + end + end +end + +class ApplicationController + prepend ApplicationControllerCWExtension if SiteSetting.custom_wizard_enabled +end \ No newline at end of file diff --git a/controllers/extra_locales_controller.rb b/controllers/extra_locales_controller.rb new file mode 100644 index 00000000..e1da1803 --- /dev/null +++ b/controllers/extra_locales_controller.rb @@ -0,0 +1,20 @@ +module CustomWizardExtraLocalesController + def show + if request.referer && URI(request.referer).path.include?('/w/') + bundle = params[:bundle] + + if params[:v]&.size == 32 + hash = ExtraLocalesController.bundle_js_hash(bundle) + immutable_for(1.year) if hash == params[:v] + end + + render plain: ExtraLocalesController.bundle_js(bundle), content_type: "application/javascript" + else + super + end + end +end + +class ExtraLocalesController + prepend CustomWizardExtraLocalesController if SiteSetting.custom_wizard_enabled +end \ No newline at end of file diff --git a/controllers/invites_controller.rb b/controllers/invites_controller.rb new file mode 100644 index 00000000..760100f2 --- /dev/null +++ b/controllers/invites_controller.rb @@ -0,0 +1,22 @@ +module InvitesControllerCustomWizard + def path(url) + if Wizard.user_requires_completion?(@user) + wizard_id = @user.custom_fields['custom_wizard_redirect'] + + if wizard_id && url != '/' + CustomWizard::Wizard.set_submission_redirect(@user, wizard_id, url) + url = "/w/#{wizard_id.dasherize}" + end + end + super(url) + end + + private def post_process_invite(user) + super(user) + @user = user + end +end + +class InvitesController + prepend InvitesControllerCustomWizard if SiteSetting.custom_wizard_enabled +end \ No newline at end of file diff --git a/lib/wizard/choice.rb b/lib/wizard/choice.rb new file mode 100644 index 00000000..f0aa6bf3 --- /dev/null +++ b/lib/wizard/choice.rb @@ -0,0 +1,17 @@ +module CustomWizardChoiceExtension + def initialize(id, opts) + @id = id + @opts = opts + @data = opts[:data] + @extra_label = opts[:extra_label] + @icon = opts[:icon] + end + + def label + @label ||= PrettyText.cook(@opts[:label]) + end +end + +class Wizard::Choice + prepend CustomWizardChoiceExtension if SiteSetting.custom_wizard_enabled +end \ No newline at end of file diff --git a/lib/wizard/field.rb b/lib/wizard/field.rb new file mode 100644 index 00000000..038fdbbc --- /dev/null +++ b/lib/wizard/field.rb @@ -0,0 +1,37 @@ +module CustomWizardFieldExtension + attr_reader :label, + :description, + :image, + :key, + :min_length, + :file_types, + :limit, + :property + + attr_accessor :dropdown_none + + def initialize(attrs) + @attrs = attrs || {} + @id = attrs[:id] + @type = attrs[:type] + @required = !!attrs[:required] + @description = attrs[:description] + @image = attrs[:image] + @key = attrs[:key] + @min_length = attrs[:min_length] + @value = attrs[:value] + @choices = [] + @dropdown_none = attrs[:dropdown_none] + @file_types = attrs[:file_types] + @limit = attrs[:limit] + @property = attrs[:property] + end + + def label + @label ||= PrettyText.cook(@attrs[:label]) + end +end + +class Wizard::Field + prepend CustomWizardFieldExtension if SiteSetting.custom_wizard_enabled +end \ No newline at end of file diff --git a/lib/wizard/step.rb b/lib/wizard/step.rb new file mode 100644 index 00000000..b58bc837 --- /dev/null +++ b/lib/wizard/step.rb @@ -0,0 +1,7 @@ +module CustomWizardStepExtension + attr_accessor :title, :description, :key, :permitted, :permitted_message +end + +class Wizard::Step + prepend CustomWizardStepExtension if SiteSetting.custom_wizard_enabled +end \ No newline at end of file diff --git a/plugin.rb b/plugin.rb index a02013e0..e7168536 100644 --- a/plugin.rb +++ b/plugin.rb @@ -11,8 +11,9 @@ register_asset 'lib/jquery.timepicker.scss' enabled_site_setting :custom_wizard_enabled config = Rails.application.config -config.assets.paths << Rails.root.join('plugins', 'discourse-custom-wizard', 'assets', 'javascripts') -config.assets.paths << Rails.root.join('plugins', 'discourse-custom-wizard', 'assets', 'stylesheets', 'wizard') +plugin_asset_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets" +config.assets.paths << "#{plugin_asset_path}/javascripts" +config.assets.paths << "#{plugin_asset_path}/stylesheets/wizard" if Rails.env.production? config.assets.precompile += %w{ @@ -44,6 +45,9 @@ after_initialize do '../controllers/custom_wizard/admin.rb', '../controllers/custom_wizard/transfer.rb', '../controllers/custom_wizard/api.rb', + '../controllers/application_controller.rb', + '../controllers/extra_locales_controller.rb', + '../controllers/invites_controller.rb', '../jobs/clear_after_time_wizard.rb', '../jobs/refresh_api_access_token.rb', '../jobs/set_after_time_wizard.rb', @@ -57,342 +61,61 @@ after_initialize do '../lib/custom_wizard/api/authorization.rb', '../lib/custom_wizard/api/endpoint.rb', '../lib/custom_wizard/api/log_entry.rb', + '../lib/wizard/choice.rb', + '../lib/wizard/field.rb', + '../lib/wizard/step.rb', '../serializers/custom_wizard/api_serializer.rb', '../serializers/custom_wizard/basic_api_serializer.rb', '../serializers/custom_wizard/api/authorization_serializer.rb', '../serializers/custom_wizard/api/basic_endpoint_serializer.rb', '../serializers/custom_wizard/api/endpoint_serializer.rb', - '../serializers/custom_wizard/api/log_serializer.rb' + '../serializers/custom_wizard/api/log_serializer.rb', + '../serializers/site_serializer.rb', + '../serializers/wizard_serializer.rb', + '../serializers/wizard_step_serializer.rb', + '../serializers/wizard_field_serializer.rb' ].each do |path| load File.expand_path(path, __FILE__) end - ::Wizard.class_eval do - def self.user_requires_completion?(user) - wizard_result = self.new(user).requires_completion? - return wizard_result if wizard_result + add_class_method(:wizard, :user_requires_completion?) do |user| + wizard_result = self.new(user).requires_completion? + return wizard_result if wizard_result - custom_redirect = false + custom_redirect = false - if user && user.first_seen_at.blank? && wizard_id = CustomWizard::Wizard.after_signup - wizard = CustomWizard::Wizard.create(user, wizard_id) + if user && + user.first_seen_at.blank? && + wizard_id = CustomWizard::Wizard.after_signup + + wizard = CustomWizard::Wizard.create(user, wizard_id) - if !wizard.completed? && wizard.permitted? - custom_redirect = true - CustomWizard::Wizard.set_wizard_redirect(user, wizard_id) - end - end - - !!custom_redirect - end - end - - ::Wizard::Field.class_eval do - attr_reader :label, :description, :image, :key, :min_length, :file_types, :limit, :property - attr_accessor :dropdown_none - - def initialize(attrs) - @attrs = attrs || {} - @id = attrs[:id] - @type = attrs[:type] - @required = !!attrs[:required] - @description = attrs[:description] - @image = attrs[:image] - @key = attrs[:key] - @min_length = attrs[:min_length] - @value = attrs[:value] - @choices = [] - @dropdown_none = attrs[:dropdown_none] - @file_types = attrs[:file_types] - @limit = attrs[:limit] - @property = attrs[:property] - end - - def label - @label ||= PrettyText.cook(@attrs[:label]) - end - end - - ::Wizard::Choice.class_eval do - def initialize(id, opts) - @id = id - @opts = opts - @data = opts[:data] - @extra_label = opts[:extra_label] - @icon = opts[:icon] - end - - def label - @label ||= PrettyText.cook(@opts[:label]) - end - end - - class ::Wizard::Step - attr_accessor :title, :description, :key, :permitted, :permitted_message - end - - ::WizardSerializer.class_eval do - attributes :id, - :name, - :background, - :completed, - :required, - :min_trust, - :permitted, - :user, - :categories, - :uncategorized_category_id - - def id - object.id - end - - def include_id? - object.respond_to?(:id) - end - - def name - object.name - end - - def include_name? - object.respond_to?(:name) - end - - def background - object.background - end - - def include_background? - object.respond_to?(:background) - end - - def completed - object.completed? - end - - def include_completed? - object.completed? && - (!object.respond_to?(:multiple_submissions) || !object.multiple_submissions) && - !scope.is_admin? - end - - def min_trust - object.min_trust - end - - def include_min_trust? - object.respond_to?(:min_trust) - end - - def permitted - object.permitted? - end - - def include_permitted? - object.respond_to?(:permitted?) - end - - def include_start? - object.start && include_steps? - end - - def include_steps? - !include_completed? - end - - def required - object.required - end - - def include_required? - object.respond_to?(:required) - end - - def user - object.user - end - - def categories - begin - site = ::Site.new(scope) - ::ActiveModel::ArraySerializer.new(site.categories, each_serializer: BasicCategorySerializer) - rescue => e - puts "HERE IS THE ERROR: #{e.inspect}" - end - end - - def uncategorized_category_id - SiteSetting.uncategorized_category_id - end - end - - ::WizardStepSerializer.class_eval do - attributes :permitted, :permitted_message - - def title - return PrettyText.cook(object.title) if object.title - PrettyText.cook(I18n.t("#{object.key || i18n_key}.title", default: '')) - 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 permitted - object.permitted - end - - def permitted_message - object.permitted_message - end - end - - ::WizardFieldSerializer.class_eval do - attributes :dropdown_none, :image, :file_types, :limit, :property - - def label - return object.label if object.label.present? - I18n.t("#{object.key || i18n_key}.label", default: '') - 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 image - object.image - end - - def include_image? - object.image.present? - end - - def placeholder - I18n.t("#{object.key || i18n_key}.placeholder", default: '') - end - - def dropdown_none - object.dropdown_none - end - - def file_types - object.file_types - end - - def limit - object.limit - end - - def property - object.property - end - end - - ::UsersController.class_eval do - def wizard_path - if custom_wizard_redirect = current_user.custom_fields['redirect_to_wizard'] - "#{Discourse.base_url}/w/#{custom_wizard_redirect.dasherize}" - else - "#{Discourse.base_url}/wizard" - end - end - end - - module InvitesControllerCustomWizard - def path(url) - if Wizard.user_requires_completion?(@user) - wizard_id = @user.custom_fields['custom_wizard_redirect'] - - if wizard_id && url != '/' - CustomWizard::Wizard.set_submission_redirect(@user, wizard_id, url) - url = "/w/#{wizard_id.dasherize}" - end - end - super(url) - end - - private def post_process_invite(user) - super(user) - @user = user - end - end - - require_dependency 'invites_controller' - class ::InvitesController - prepend InvitesControllerCustomWizard - end - - require_dependency 'application_controller' - class ::ApplicationController - before_action :redirect_to_wizard_if_required, if: :current_user - - def redirect_to_wizard_if_required - wizard_id = current_user.custom_fields['redirect_to_wizard'] - @excluded_routes ||= SiteSetting.wizard_redirect_exclude_paths.split('|') + ['/w/'] - url = request.referer || request.original_url - - if request.format === 'text/html' && !@excluded_routes.any? {|str| /#{str}/ =~ url} && wizard_id - if request.referer !~ /\/w\// && request.referer !~ /\/invites\// - CustomWizard::Wizard.set_submission_redirect(current_user, wizard_id, request.referer) - end - - if CustomWizard::Wizard.exists?(wizard_id) - redirect_to "/w/#{wizard_id.dasherize}" - end - end - end - end - - add_to_serializer(:current_user, :redirect_to_wizard) {object.custom_fields['redirect_to_wizard']} - - ## TODO limit this to the first admin - SiteSerializer.class_eval do - attributes :complete_custom_wizard - - def include_wizard_required? - scope.is_admin? && Wizard.new(scope.user).requires_completion? - end - - def complete_custom_wizard - if scope.user && requires_completion = CustomWizard::Wizard.prompt_completion(scope.user) - requires_completion.map {|w| {name: w[:name], url: "/w/#{w[:id]}"}} + if !wizard.completed? && wizard.permitted? + custom_redirect = true + CustomWizard::Wizard.set_wizard_redirect(user, wizard_id) end end - def include_complete_custom_wizard? - complete_custom_wizard.present? + !!custom_redirect + end + + add_to_class(:users_controller, :wizard_path) do + if custom_wizard_redirect = current_user.custom_fields['redirect_to_wizard'] + "#{Discourse.base_url}/w/#{custom_wizard_redirect.dasherize}" + else + "#{Discourse.base_url}/wizard" end end + add_to_serializer(:current_user, :redirect_to_wizard) do + object.custom_fields['redirect_to_wizard'] + end + DiscourseEvent.on(:user_approved) do |user| if wizard_id = CustomWizard::Wizard.after_signup CustomWizard::Wizard.set_wizard_redirect(user, wizard_id) end end - - module CustomWizardExtraLocalesController - def show - if request.referer && URI(request.referer).path.include?('/w/') - bundle = params[:bundle] - - if params[:v]&.size == 32 - hash = ExtraLocalesController.bundle_js_hash(bundle) - immutable_for(1.year) if hash == params[:v] - end - - render plain: ExtraLocalesController.bundle_js(bundle), content_type: "application/javascript" - else - super - end - end - end - - class ::ExtraLocalesController - prepend CustomWizardExtraLocalesController - end DiscourseEvent.trigger(:custom_wizard_ready) end diff --git a/serializers/site_serializer.rb b/serializers/site_serializer.rb new file mode 100644 index 00000000..21b996f6 --- /dev/null +++ b/serializers/site_serializer.rb @@ -0,0 +1,22 @@ +## TODO limit this to the first admin +module SiteSerializerCWX + attributes :complete_custom_wizard + + def include_wizard_required? + scope.is_admin? && Wizard.new(scope.user).requires_completion? + end + + def complete_custom_wizard + if scope.user && requires_completion = CustomWizard::Wizard.prompt_completion(scope.user) + requires_completion.map {|w| {name: w[:name], url: "/w/#{w[:id]}"}} + end + end + + def include_complete_custom_wizard? + complete_custom_wizard.present? + end +end + +class SiteSerializer + prepend SiteSerializerCWX if SiteSetting.custom_wizard_enabled +end \ No newline at end of file diff --git a/serializers/wizard_field_serializer.rb b/serializers/wizard_field_serializer.rb new file mode 100644 index 00000000..1749fb89 --- /dev/null +++ b/serializers/wizard_field_serializer.rb @@ -0,0 +1,45 @@ +module CustomWizardWizardFieldSerializerExtension + attributes :dropdown_none, :image, :file_types, :limit, :property + + def label + return object.label if object.label.present? + I18n.t("#{object.key || i18n_key}.label", default: '') + 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 image + object.image + end + + def include_image? + object.image.present? + end + + def placeholder + I18n.t("#{object.key || i18n_key}.placeholder", default: '') + end + + def dropdown_none + object.dropdown_none + end + + def file_types + object.file_types + end + + def limit + object.limit + end + + def property + object.property + end +end + +class WizardFieldSerializer + prepend CustomWizardWizardFieldSerializerExtension if SiteSetting.custom_wizard_enabled +end \ No newline at end of file diff --git a/serializers/wizard_serializer.rb b/serializers/wizard_serializer.rb new file mode 100644 index 00000000..18659417 --- /dev/null +++ b/serializers/wizard_serializer.rb @@ -0,0 +1,99 @@ +module CustomWizardWizardSerializerExtension + attributes :id, + :name, + :background, + :completed, + :required, + :min_trust, + :permitted, + :user, + :categories, + :uncategorized_category_id + + def id + object.id + end + + def include_id? + object.respond_to?(:id) + end + + def name + object.name + end + + def include_name? + object.respond_to?(:name) + end + + def background + object.background + end + + def include_background? + object.respond_to?(:background) + end + + def completed + object.completed? + end + + def include_completed? + object.completed? && + (!object.respond_to?(:multiple_submissions) || !object.multiple_submissions) && + !scope.is_admin? + end + + def min_trust + object.min_trust + end + + def include_min_trust? + object.respond_to?(:min_trust) + end + + def permitted + object.permitted? + end + + def include_permitted? + object.respond_to?(:permitted?) + end + + def include_start? + object.start && include_steps? + end + + def include_steps? + !include_completed? + end + + def required + object.required + end + + def include_required? + object.respond_to?(:required) + end + + def user + object.user + end + + def categories + begin + site = ::Site.new(scope) + ::ActiveModel::ArraySerializer.new(site.categories, each_serializer: BasicCategorySerializer) + rescue => e + puts "HERE IS THE ERROR: #{e.inspect}" + end + end + + def uncategorized_category_id + SiteSetting.uncategorized_category_id + end +end + +class WizardSerializer + prepend CustomWizardWizardSerializerExtension if SiteSetting.custom_wizard_enabled +end \ No newline at end of file diff --git a/serializers/wizard_step_serializer.rb b/serializers/wizard_step_serializer.rb new file mode 100644 index 00000000..0ee28202 --- /dev/null +++ b/serializers/wizard_step_serializer.rb @@ -0,0 +1,25 @@ +module CustomWizardWizardStepSerializerExtension + attributes :permitted, :permitted_message + + def title + return PrettyText.cook(object.title) if object.title + PrettyText.cook(I18n.t("#{object.key || i18n_key}.title", default: '')) + 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 permitted + object.permitted + end + + def permitted_message + object.permitted_message + end +end + +class WizardStepSerializer + prepend CustomWizardWizardStepSerializerExtension if SiteSetting.custom_wizard_enabled +end \ No newline at end of file From a96a2b965c20e46387af6d45092b1aece11cbb23 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Fri, 6 Dec 2019 20:05:19 +1100 Subject: [PATCH 4/9] Bulder tests WIP --- lib/custom_wizard/builder.rb | 11 +-- lib/custom_wizard/wizard.rb | 4 +- serializers/site_serializer.rb | 7 +- serializers/wizard_field_serializer.rb | 6 +- serializers/wizard_serializer.rb | 24 +++--- serializers/wizard_step_serializer.rb | 6 +- spec/components/custom_wizard/builder_spec.rb | 81 ++++++++++++++----- spec/fixtures/wizard.json | 26 +++--- 8 files changed, 111 insertions(+), 54 deletions(-) diff --git a/lib/custom_wizard/builder.rb b/lib/custom_wizard/builder.rb index abdc4cd1..a9157c2b 100644 --- a/lib/custom_wizard/builder.rb +++ b/lib/custom_wizard/builder.rb @@ -98,9 +98,10 @@ class CustomWizard::Builder if permitted_params = step_template['permitted_params'] permitted_data = {} - permitted_params.each do |param| - key = param['key'].to_sym - permitted_data[key] = params[key] if params[key] + permitted_params.each do |p| + params_key = p['key'].to_sym + submission_key = p['value'].to_sym + permitted_data[submission_key] = params[params_key] if params[params_key] end if permitted_data.present? @@ -215,7 +216,7 @@ class CustomWizard::Builder submission = @submissions.last params[:value] = submission[field_template['id']] if submission[field_template['id']] end - + ## If a field updates a profile field, load the current value if step_template['actions'] && step_template['actions'].any? profile_actions = step_template['actions'].select { |a| a['type'] === 'update_profile' } @@ -223,7 +224,7 @@ class CustomWizard::Builder if profile_actions.any? profile_actions.each do |action| if update = action['profile_updates'].select { |u| u['key'] === field_template['id'] }.first - params[:value] = prefill_profile_field(update) + params[:value] = prefill_profile_field(update) || params[:value] end end end diff --git a/lib/custom_wizard/wizard.rb b/lib/custom_wizard/wizard.rb index b8d0f7af..26f1c79a 100644 --- a/lib/custom_wizard/wizard.rb +++ b/lib/custom_wizard/wizard.rb @@ -192,8 +192,8 @@ class CustomWizard::Wizard end end - def self.add_wizard(json) - wizard = ::JSON.parse(json) + def self.add_wizard(obj) + wizard = obj.is_a?(String) ? ::JSON.parse(json) : obj PluginStore.set('custom_wizard', wizard["id"], wizard) end diff --git a/serializers/site_serializer.rb b/serializers/site_serializer.rb index 21b996f6..faff37c9 100644 --- a/serializers/site_serializer.rb +++ b/serializers/site_serializer.rb @@ -1,6 +1,11 @@ ## TODO limit this to the first admin module SiteSerializerCWX - attributes :complete_custom_wizard + extend ActiveSupport::Concern + + included do + attributes :complete_custom_wizard + end + def include_wizard_required? scope.is_admin? && Wizard.new(scope.user).requires_completion? diff --git a/serializers/wizard_field_serializer.rb b/serializers/wizard_field_serializer.rb index 1749fb89..c1ef779f 100644 --- a/serializers/wizard_field_serializer.rb +++ b/serializers/wizard_field_serializer.rb @@ -1,5 +1,9 @@ module CustomWizardWizardFieldSerializerExtension - attributes :dropdown_none, :image, :file_types, :limit, :property + extend ActiveSupport::Concern + + included do + attributes :dropdown_none, :image, :file_types, :limit, :property + end def label return object.label if object.label.present? diff --git a/serializers/wizard_serializer.rb b/serializers/wizard_serializer.rb index 18659417..9cd97f26 100644 --- a/serializers/wizard_serializer.rb +++ b/serializers/wizard_serializer.rb @@ -1,14 +1,18 @@ module CustomWizardWizardSerializerExtension - attributes :id, - :name, - :background, - :completed, - :required, - :min_trust, - :permitted, - :user, - :categories, - :uncategorized_category_id + extend ActiveSupport::Concern + + included do + attributes :id, + :name, + :background, + :completed, + :required, + :min_trust, + :permitted, + :user, + :categories, + :uncategorized_category_id + end def id object.id diff --git a/serializers/wizard_step_serializer.rb b/serializers/wizard_step_serializer.rb index 0ee28202..e3774069 100644 --- a/serializers/wizard_step_serializer.rb +++ b/serializers/wizard_step_serializer.rb @@ -1,5 +1,9 @@ module CustomWizardWizardStepSerializerExtension - attributes :permitted, :permitted_message + extend ActiveSupport::Concern + + included do + attributes :permitted, :permitted_message + end def title return PrettyText.cook(object.title) if object.title diff --git a/spec/components/custom_wizard/builder_spec.rb b/spec/components/custom_wizard/builder_spec.rb index 5d0a8c73..b5be4ca4 100644 --- a/spec/components/custom_wizard/builder_spec.rb +++ b/spec/components/custom_wizard/builder_spec.rb @@ -3,18 +3,30 @@ require 'rails_helper' describe CustomWizard::Builder do - fab!(:user) { Fabricate(:user) } + fab!(:user) { Fabricate(:user) } + fab!(:trusted_user) { Fabricate(:user, trust_level: 3)} - before do - @template = File.open( + let!(:template) do + JSON.parse(File.open( "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" - ).read + ).read) + end + + def build_wizard(t = template, u = user, build_opts = {}, params = {}) + CustomWizard::Wizard.add_wizard(t) + CustomWizard::Builder.new(u, 'welcome').build(build_opts, params) + end + + def add_submission_data + PluginStore.set("welcome_submissions", user.id, + name: 'Angus', + website: 'https://thepavilion.io' + ) end it "returns no steps when disabled" do - CustomWizard::Wizard.add_wizard(@template) SiteSetting.custom_wizard_enabled = false - wizard = CustomWizard::Builder.new(user, 'welcome').build + wizard = build_wizard expect(wizard.steps.length).to eq(0) expect(wizard.name).to eq('Welcome') end @@ -24,38 +36,63 @@ describe CustomWizard::Builder do SiteSetting.custom_wizard_enabled = true end - it "returns steps when enabled" do - CustomWizard::Wizard.add_wizard(@template) - wizard = CustomWizard::Builder.new(user, 'welcome').build - expect(wizard.steps.length).to eq(2) + it "returns steps" do + expect(build_wizard.steps.length).to eq(2) end - it 'returns no steps if the multiple submissions are disabled and user has completed' do - @template['multiple_submissions'] = false - CustomWizard::Wizard.add_wizard(@template) - wizard = CustomWizard::Builder.new(user, 'welcome').build - expect(wizard.steps.length).to eq(0) + it 'returns no steps if the multiple submissions are disabled and user has completed it' do + history_params = { + action: UserHistory.actions[:custom_wizard_step], + acting_user_id: user.id, + context: template['id'] + } + UserHistory.create!(history_params.merge(subject: template['steps'][0]['id'])) + UserHistory.create!(history_params.merge(subject: template['steps'][1]['id'])) + + template["multiple_submissions"] = false + expect(build_wizard(template).steps.length).to eq(0) end - it 'returns nothing if the user is not permitted to see it' do - + it 'returns no steps if has min trust and user does not meet it' do + template["min_trust"] = 3 + expect(build_wizard(template).steps.length).to eq(0) end - it 'returns a wizard with prefilled data if user has partially completed' do + it 'returns steps if it has min trust and user meets it' do + template["min_trust"] = 3 + expect(build_wizard(template, trusted_user).steps.length).to eq(2) + end + it 'returns a wizard with prefilled data if user has partially completed it' do + add_submission_data + wizard = build_wizard + expect(wizard.steps[0].fields.first.value).to eq('Angus') + expect(wizard.steps[1].fields.first.value).to eq('https://thepavilion.io') end it 'returns a wizard with no prefilled data if options include reset' do - + add_submission_data + wizard = build_wizard(template, user, reset: true) + expect(wizard.steps[0].fields.first.value).to eq(nil) + expect(wizard.steps[1].fields.first.value).to eq(nil) end context 'building steps' do - it 'returns step data correctly' do - + it 'returns step metadata' do + expect(build_wizard.steps[0].title).to eq('Welcome to Pavilion') + expect(build_wizard.steps[1].title).to eq('Tell us about you') end it 'saves permitted params' do - + template['steps'][0]['permitted_params'] = [ + { + "key": "param_key", + "value": "submission_param_key" + } + ] + wizard = build_wizard(template, user, {}, param_key: 'param_value') + submissions = PluginStore.get("welcome_submissions", user.id) + expect(submissions.first['submission_param_key']).to eq('param_value') end it 'ensures required data is present' do diff --git a/spec/fixtures/wizard.json b/spec/fixtures/wizard.json index e384427b..e657cb3c 100644 --- a/spec/fixtures/wizard.json +++ b/spec/fixtures/wizard.json @@ -8,24 +8,27 @@ "theme_id": 4, "steps": [ { - "id": "step_1", + "id": "welcome", "title": "Welcome to Pavilion", "raw_description": "Hey there, thanks for signing up.\n\nWe're Pavilion, an international freelancer cooperative that specialises in online communities.\n\nThis site is our own community, where we work with our clients, users of our open source work and our broader community.\n\n", - "description": "

Hey there, thanks for signing up.

\n

We’re Pavilion, an international freelancer cooperative that specialises in online communities.

\n

This site is our own community, where we work with our clients, users of our open source work and our broader community.

" - }, - { - "id": "step_2", - "title": "Tell us about you", - "raw_description": "We'd like to know a little more about you. Add a your name and your website below. This will update your user profile.", + "description": "

Hey there, thanks for signing up.

\n

We’re Pavilion, an international freelancer cooperative that specialises in online communities.

\n

This site is our own community, where we work with our clients, users of our open source work and our broader community.

", "fields": [ { "id": "name", "type": "text", "label": "Name", "required": true - }, + } + ] + }, + { + "id": "about_you", + "title": "Tell us about you", + "raw_description": "We'd like to know a little more about you. Add a your name and your website below. This will update your user profile.", + "description": "

We’d like to know a little more about you. Add a your name and your website below. This will update your user profile.

", + "fields": [ { - "id": "field_3", + "id": "website", "label": "Website", "type": "text" } @@ -40,13 +43,12 @@ "value": "name" }, { - "key": "field_3", + "key": "website", "value": "website" } ] } - ], - "description": "

We’d like to know a little more about you. Add a your name and your website below. This will update your user profile.

" + ] } ] } \ No newline at end of file From be83350b72c9e8e3cda7dcd441c261aacf305ff1 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 9 Dec 2019 12:43:30 +1100 Subject: [PATCH 5/9] Tests WIP --- config/locales/server.en.yml | 1 + lib/custom_wizard/builder.rb | 8 +- lib/custom_wizard/step_updater.rb | 2 +- spec/components/custom_wizard/builder_spec.rb | 121 +++++++++++------- 4 files changed, 81 insertions(+), 51 deletions(-) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index e8e4a7c1..24322b98 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -8,6 +8,7 @@ en: custom_title: "Wizard" field: too_short: "%{label} must be at least %{min} characters" + required: "%{label} is required." none: "We couldn't find a wizard at that address." no_skip: "Wizard can't be skipped" export: diff --git a/lib/custom_wizard/builder.rb b/lib/custom_wizard/builder.rb index a9157c2b..53cbe4d3 100644 --- a/lib/custom_wizard/builder.rb +++ b/lib/custom_wizard/builder.rb @@ -154,7 +154,7 @@ class CustomWizard::Builder next if updater.errors.any? - data = updater.fields.to_h + data = updater.fields ## if the wizard has data from the previous steps make that accessible to the actions. if @submissions && @submissions.last && !@submissions.last.key?("submitted_at") @@ -324,13 +324,17 @@ class CustomWizard::Builder def validate_field(field, updater, step_template) value = updater.fields[field['id']] min_length = false + label = field['label'] || I18n.t("#{field['key']}.label") + + if field['required'] && !value + updater.errors.add(field['id'].to_s, I18n.t('wizard.field.required', label: label)) + end if is_text_type(field) min_length = field['min_length'] end if min_length && value.is_a?(String) && value.strip.length < min_length.to_i - label = field['label'] || I18n.t("#{field['key']}.label") updater.errors.add(field['id'].to_s, I18n.t('wizard.field.too_short', label: label, min: min_length.to_i)) end diff --git a/lib/custom_wizard/step_updater.rb b/lib/custom_wizard/step_updater.rb index c521924c..80428202 100644 --- a/lib/custom_wizard/step_updater.rb +++ b/lib/custom_wizard/step_updater.rb @@ -8,7 +8,7 @@ class CustomWizard::StepUpdater @wizard = wizard @step = step @refresh_required = false - @fields = fields + @fields = fields.to_h.with_indifferent_access @result = {} end diff --git a/spec/components/custom_wizard/builder_spec.rb b/spec/components/custom_wizard/builder_spec.rb index b5be4ca4..22d3a708 100644 --- a/spec/components/custom_wizard/builder_spec.rb +++ b/spec/components/custom_wizard/builder_spec.rb @@ -12,16 +12,40 @@ describe CustomWizard::Builder do ).read) end + let(:permitted_params) {[{"key":"param_key","value":"submission_param_key"}]} + let(:required_data) {[{"key":"nickname","connector":"equals","value":"name"}]} + let(:required_data_message) {"Nickname is required to match your name"} + let(:checkbox_field) {{"id":"checkbox","type":"checkbox","label":"Checkbox"}} + let(:composer_field) {{"id": "composer","label":"Composer","type":"composer"}} + let(:dropdown_categories_field) {{"id": "dropdown_categories","type": "dropdown","label": "Dropdown Categories","choices_type": "preset","choices_preset": "categories"}} + let(:tag_field) {{"id": "tag","type": "tag","label": "Tag","limit": "2"}} + let(:category_field) {{"id": "category","type": "category","limit": "1","label": "Category"}} + let(:image_field) {{"id": "image","type": "image","label": "Image"}} + let(:text_field) {{"id": "text","type": "text","min_length": "12","label": "Text"}} + let(:textarea_field) {{"id": "textarea","type": "textarea","min_length": "40","label": "Textarea"}} + let(:text_only_field) {{"id": "text_only","type": "text-only","label": "Text only"}} + let(:upload_field) {{"id": "upload","type": "upload","file_types": ".jpg,.png,.pdf","label": "Upload"}} + let(:user_selector_field) {{"id": "user_selector","type": "user-selector","label": "User selector"}} + let(:dropdown_groups_field) {{"id": "dropdown_groups","type": "dropdown","choices_type": "preset","choices_preset": "groups","label": "Dropdown Groups"}} + let(:dropdown_tags_field) {{"id": "dropdown_tags","type": "dropdown","choices_type": "preset","choices_preset": "tags","label": "Dropdown Tags"}} + let(:dropdown_custom_field) {{"id": "dropdown_custom","type": "dropdown","choices_type": "custom","choices": [{"key": "option_1","value": "Option 1"},{"key": "option_2","value": "Option 2"}]}} + let(:dropdown_translation_field) {{"id": "dropdown_translation","type": "dropdown","choices_type": "translation","choices_key": "key1.key2"}} + let(:dropdown_categories_filtered_field) {{"id": "dropdown_categories_filtered_field","type": "dropdown","choices_type": "preset","choices_preset": "categories","choices_filters": [{"key": "slug","value": "staff"}]}} + def build_wizard(t = template, u = user, build_opts = {}, params = {}) CustomWizard::Wizard.add_wizard(t) CustomWizard::Builder.new(u, 'welcome').build(build_opts, params) end - def add_submission_data - PluginStore.set("welcome_submissions", user.id, + def add_submission_data(data = {}) + PluginStore.set("welcome_submissions", user.id, { name: 'Angus', website: 'https://thepavilion.io' - ) + }.merge(data)) + end + + def get_submission_data + PluginStore.get("welcome_submissions", user.id) end it "returns no steps when disabled" do @@ -84,76 +108,77 @@ describe CustomWizard::Builder do end it 'saves permitted params' do - template['steps'][0]['permitted_params'] = [ - { - "key": "param_key", - "value": "submission_param_key" - } - ] + template['steps'][0]['permitted_params'] = permitted_params wizard = build_wizard(template, user, {}, param_key: 'param_value') - submissions = PluginStore.get("welcome_submissions", user.id) + submissions = get_submission_data expect(submissions.first['submission_param_key']).to eq('param_value') end - it 'ensures required data is present' do - - end - end - - context 'building fields' do - it 'returns field data correctly' do - + it 'is not permitted if required data is not present' do + template['steps'][0]['required_data'] = required_data + expect(build_wizard(template, user).steps[0].permitted).to eq(false) end - it 'returns checkbox fields correctly' do - + it "is not permitted if required data is not present" do + template['steps'][0]['required_data'] = required_data + add_submission_data(nickname: "John") + expect(build_wizard(template, user).steps[0].permitted).to eq(false) end - it 'returns upload fields correctly' do - + it 'it shows required data message if required data has message' do + template['steps'][0]['required_data'] = required_data + template['steps'][0]['required_data_message'] = required_data_message + add_submission_data(nickname: "John") + wizard = build_wizard(template, user) + expect(wizard.steps[0].permitted).to eq(false) + expect(wizard.steps[0].permitted_message).to eq(required_data_message) end - it 'returns category fields correctly' do - + it 'is permitted if required data is present' do + template['steps'][0]['required_data'] = required_data + PluginStore.set('welcome_submissions', user.id, nickname: "Angus", name: "Angus") + expect(build_wizard(template, user).steps[0].permitted).to eq(true) end - it 'returns tag fields correctly' do - + it 'returns field metadata' do + expect(build_wizard(template, user).steps[0].fields[0].label).to eq("

Name

") + expect(build_wizard(template, user).steps[0].fields[0].type).to eq("text") end - it 'returns custom dropdown fields correctly' do - - end - - it 'returns translated dropdown fields correctly' do - - end - - it 'returns preset dropdown fields correctly' do - - end - - it 'applies preset dropdown filters correctly' do - - end - - it 'prefils profile data correctly' do - + it 'returns fields' do + template['steps'][0]['fields'][1] = checkbox_field + expect(build_wizard(template, user).steps[0].fields.length).to eq(2) end end context 'on update' do context 'validation' do - it 'applies min length correctly' do - + it 'applies min length' do + template['steps'][0]['fields'][0]['min_length'] = 10 + wizard = build_wizard(template, user) + updater = wizard.create_updater(template['steps'][0]['id'], name: 'short') + updater.update + expect(updater.errors.messages[:name].first).to eq( + I18n.t('wizard.field.too_short', label: 'Name', min: 10) + ) end it 'standardises boolean entries' do - + template['steps'][0]['fields'][0] = checkbox_field + wizard = build_wizard(template, user) + updater = wizard.create_updater(template['steps'][0]['id'], checkbox: 'false') + updater.update + submissions = get_submission_data + expect(submissions.first['checkbox']).to eq(false) end it 'requires required fields' do - ## this may require additional work? + wizard = build_wizard + updater = wizard.create_updater(template['steps'][0]['id'], other_field: 'other') + updater.update + expect(updater.errors.messages[:name].first).to eq( + I18n.t('wizard.field.required', label: 'Name') + ) end context 'submisisons' do From 8e5d6b779c239eed560f5d3aba1486ac66b5dc6b Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 9 Dec 2019 16:51:42 +1100 Subject: [PATCH 6/9] Complete builder.rb spec --- lib/custom_wizard/builder.rb | 4 +- lib/custom_wizard/step_updater.rb | 2 + spec/components/custom_wizard/builder_spec.rb | 230 ++++++++++++++---- spec/fixtures/wizard.json | 7 +- 4 files changed, 183 insertions(+), 60 deletions(-) diff --git a/lib/custom_wizard/builder.rb b/lib/custom_wizard/builder.rb index 53cbe4d3..5e52c38f 100644 --- a/lib/custom_wizard/builder.rb +++ b/lib/custom_wizard/builder.rb @@ -161,7 +161,7 @@ class CustomWizard::Builder submission = @submissions.last data = submission.merge(data) end - + if step_template['actions'] && step_template['actions'].length && data step_template['actions'].each do |action| self.send(action['type'].to_sym, user, action, data) @@ -364,7 +364,7 @@ class CustomWizard::Builder else title = data[action['title']] end - + if action['post_builder'] post = CustomWizard::Builder.fill_placeholders(action['post_template'], user, data) else diff --git a/lib/custom_wizard/step_updater.rb b/lib/custom_wizard/step_updater.rb index 80428202..b5f9bf6c 100644 --- a/lib/custom_wizard/step_updater.rb +++ b/lib/custom_wizard/step_updater.rb @@ -13,6 +13,8 @@ class CustomWizard::StepUpdater end def update + return false if !SiteSetting.custom_wizard_enabled + @step.updater.call(self) if @step.present? && @step.updater.present? if success? diff --git a/spec/components/custom_wizard/builder_spec.rb b/spec/components/custom_wizard/builder_spec.rb index 22d3a708..4dc13b64 100644 --- a/spec/components/custom_wizard/builder_spec.rb +++ b/spec/components/custom_wizard/builder_spec.rb @@ -3,8 +3,9 @@ require 'rails_helper' describe CustomWizard::Builder do - fab!(:user) { Fabricate(:user) } - fab!(:trusted_user) { Fabricate(:user, trust_level: 3)} + fab!(:user) { Fabricate(:user, username: 'angus') } + fab!(:trusted_user) { Fabricate(:user, trust_level: 3) } + fab!(:group) { Fabricate(:group) } let!(:template) do JSON.parse(File.open( @@ -21,8 +22,8 @@ describe CustomWizard::Builder do let(:tag_field) {{"id": "tag","type": "tag","label": "Tag","limit": "2"}} let(:category_field) {{"id": "category","type": "category","limit": "1","label": "Category"}} let(:image_field) {{"id": "image","type": "image","label": "Image"}} - let(:text_field) {{"id": "text","type": "text","min_length": "12","label": "Text"}} - let(:textarea_field) {{"id": "textarea","type": "textarea","min_length": "40","label": "Textarea"}} + let(:text_field) {{"id": "text","type": "text","label": "Text"}} + let(:textarea_field) {{"id": "textarea","type": "textarea","label": "Textarea"}} let(:text_only_field) {{"id": "text_only","type": "text-only","label": "Text only"}} let(:upload_field) {{"id": "upload","type": "upload","file_types": ".jpg,.png,.pdf","label": "Upload"}} let(:user_selector_field) {{"id": "user_selector","type": "user-selector","label": "User selector"}} @@ -31,6 +32,11 @@ describe CustomWizard::Builder do let(:dropdown_custom_field) {{"id": "dropdown_custom","type": "dropdown","choices_type": "custom","choices": [{"key": "option_1","value": "Option 1"},{"key": "option_2","value": "Option 2"}]}} let(:dropdown_translation_field) {{"id": "dropdown_translation","type": "dropdown","choices_type": "translation","choices_key": "key1.key2"}} let(:dropdown_categories_filtered_field) {{"id": "dropdown_categories_filtered_field","type": "dropdown","choices_type": "preset","choices_preset": "categories","choices_filters": [{"key": "slug","value": "staff"}]}} + let(:create_topic_action) {{"id":"create_topic","type":"create_topic","title":"text","post":"textarea"}} + let(:send_message_action) {{"id":"send_message","type":"send_message","title":"text","post":"textarea","username":"angus"}} + let(:route_to_action) {{"id":"route_to","type":"route_to","url":"https://google.com"}} + let(:open_composer_action) {{"id":"open_composer","type":"open_composer","title":"text","post":"textarea"}} + let(:add_to_group_action) {{"id":"add_to_group","type":"add_to_group","group_id":"dropdown_groups"}} def build_wizard(t = template, u = user, build_opts = {}, params = {}) CustomWizard::Wizard.add_wizard(t) @@ -48,11 +54,44 @@ describe CustomWizard::Builder do PluginStore.get("welcome_submissions", user.id) end - it "returns no steps when disabled" do - SiteSetting.custom_wizard_enabled = false - wizard = build_wizard - expect(wizard.steps.length).to eq(0) - expect(wizard.name).to eq('Welcome') + def run_update(t = template, step_id = nil, data = {}) + wizard = build_wizard(t) + updater = wizard.create_updater(step_id || t['steps'][0]['id'], data) + updater.update + updater + end + + def send_message(extra_field = nil, extra_action_opts = {}) + fields = [text_field, textarea_field] + + if extra_field + fields.push(extra_field) + end + + template['steps'][0]['fields'] = fields + template['steps'][0]["actions"] = [send_message_action.merge(extra_action_opts)] + + run_update(template, nil, + text: "Message Title", + textarea: "message body" + ) + end + + context 'disabled' do + before do + SiteSetting.custom_wizard_enabled = false + end + + it "returns no steps" do + wizard = build_wizard + expect(wizard.steps.length).to eq(0) + expect(wizard.name).to eq('Welcome') + end + + it "doesn't save submissions" do + run_update(template, nil, name: 'Angus') + expect(get_submission_data.blank?).to eq(true) + end end context 'enabled' do @@ -152,12 +191,15 @@ describe CustomWizard::Builder do end context 'on update' do + it 'saves submissions' do + run_update(template, nil, name: 'Angus') + expect(get_submission_data.first['name']).to eq('Angus') + end + context 'validation' do it 'applies min length' do template['steps'][0]['fields'][0]['min_length'] = 10 - wizard = build_wizard(template, user) - updater = wizard.create_updater(template['steps'][0]['id'], name: 'short') - updater.update + updater = run_update(template, nil, name: 'short') expect(updater.errors.messages[:name].first).to eq( I18n.t('wizard.field.too_short', label: 'Name', min: 10) ) @@ -165,78 +207,162 @@ describe CustomWizard::Builder do it 'standardises boolean entries' do template['steps'][0]['fields'][0] = checkbox_field - wizard = build_wizard(template, user) - updater = wizard.create_updater(template['steps'][0]['id'], checkbox: 'false') - updater.update - submissions = get_submission_data - expect(submissions.first['checkbox']).to eq(false) + run_update(template, nil, checkbox: 'false') + expect(get_submission_data.first['checkbox']).to eq(false) end it 'requires required fields' do - wizard = build_wizard - updater = wizard.create_updater(template['steps'][0]['id'], other_field: 'other') - updater.update - expect(updater.errors.messages[:name].first).to eq( + template['steps'][0]['fields'][0]['required'] = true + expect(run_update(template).errors.messages[:name].first).to eq( I18n.t('wizard.field.required', label: 'Name') ) end - - context 'submisisons' do - it 'saves submissions' do - - end - - it "doesn't save submissions if disabled" do - - end - end - end - - context 'custom_step_handlers' do - it 'runs custom step handlers' do - - end end context 'actions' do - it 'runs all actions attached to a step' do - + it 'runs actions attached to a step' do + run_update(template, template['steps'][1]['id'], name: "Gus") + expect(user.name).to eq('Gus') end - it 'interpolates wizard and user data correctly' do - + it 'interpolates user data correctly' do + user.name = "Angus" + user.save! + + expect( + CustomWizard::Builder.fill_placeholders( + "My name is u{name}", + user, + {} + ) + ).to eq('My name is Angus') end it 'creates a topic' do + template['steps'][0]['fields'] = [text_field, textarea_field] + template['steps'][0]["actions"] = [create_topic_action] + updater = run_update(template, nil, + text: "Topic Title", + textarea: "topic body" + ) + topic = Topic.where(title: "Topic Title") + + expect(topic.exists?).to eq(true) + expect(Post.where( + topic_id: topic.pluck(:id), + raw: "topic body" + ).exists?).to eq(true) + end + it 'creates a topic with a custom title' do + user.name = "Angus" + user.save! + + template['steps'][0]['fields'] = [text_field, textarea_field] + + create_topic_action['custom_title_enabled'] = true + create_topic_action['custom_title'] = "u{name}' Topic Title" + template['steps'][0]["actions"] = [create_topic_action] + + run_update(template, nil, textarea: "topic body") + + topic = Topic.where(title: "Angus' Topic Title") + + expect(topic.exists?).to eq(true) + expect(Post.where( + topic_id: topic.pluck(:id), + raw: "topic body" + ).exists?).to eq(true) + end + + it 'creates a topic with a custom post' do + user.name = "Angus" + user.save! + + template['steps'][0]['fields'] = [text_field, textarea_field] + + create_topic_action['post_builder'] = true + create_topic_action['post_template'] = "u{name}' w{textarea}" + template['steps'][0]["actions"] = [create_topic_action] + + run_update(template, nil, + text: "Topic Title", + textarea: "topic body" + ) + + topic = Topic.where(title: "Topic Title") + + expect(topic.exists?).to eq(true) + expect(Post.where( + topic_id: topic.pluck(:id), + raw: "Angus' topic body" + ).exists?).to eq(true) end it 'sends a message' do - + send_message + + topic = Topic.where( + archetype: Archetype.private_message, + title: "Message Title" + ) + + expect(topic.exists?).to eq(true) + expect( + topic.first.topic_allowed_users.first.user.username + ).to eq('angus') + expect(Post.where( + topic_id: topic.pluck(:id), + raw: "message body" + ).exists?).to eq(true) end it 'doesnt sent a message if the required data is not present' do - + send_message(user_selector_field, required: "user_selector") + topic = Topic.where( + archetype: Archetype.private_message, + title: "Message Title" + ) + expect(topic.exists?).to eq(false) end it 'updates a profile' do - - end - - it 'calls an api' do - + run_update(template, template['steps'][1]['id'], name: "Sally") + expect(user.name).to eq('Sally') end it 'opens a composer' do - + template['steps'][0]['fields'] = [text_field, textarea_field] + template['steps'][0]["actions"] = [open_composer_action] + + updater = run_update(template, nil, + text: "Topic Title", + textarea: "topic body" + ) + + expect(updater.result.blank?).to eq(true) + + updater = run_update(template, template['steps'][1]['id']) + + expect(updater.result[:redirect_on_complete]).to eq( + "/new-topic?title=Topic%20Title&body=topic%20body" + ) end - it 'adds a user to a group' do - + it 'adds a user to a group' do + template['steps'][0]['fields'] = [dropdown_groups_field] + template['steps'][0]["actions"] = [add_to_group_action] + + updater = run_update(template, nil, dropdown_groups: group.id) + expect(group.users.first.username).to eq('angus') end it 're-routes a user' do - + template['steps'][0]["actions"] = [route_to_action] + updater = run_update(template, nil, {}) + expect(updater.result[:redirect_on_next]).to eq( + "https://google.com" + ) end end end diff --git a/spec/fixtures/wizard.json b/spec/fixtures/wizard.json index e657cb3c..04c16fea 100644 --- a/spec/fixtures/wizard.json +++ b/spec/fixtures/wizard.json @@ -16,8 +16,7 @@ { "id": "name", "type": "text", - "label": "Name", - "required": true + "label": "Name" } ] }, @@ -41,10 +40,6 @@ { "key": "name", "value": "name" - }, - { - "key": "website", - "value": "website" } ] } From d6621a830b3bd23ae19039afc7a402a67a38b584 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 9 Dec 2019 17:14:30 +1100 Subject: [PATCH 7/9] Add plugins-specific coverage --- .gitignore | 1 + spec/plugin_helper.rb | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 .gitignore create mode 100644 spec/plugin_helper.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..ed9f9cc1 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +coverage \ No newline at end of file diff --git a/spec/plugin_helper.rb b/spec/plugin_helper.rb new file mode 100644 index 00000000..f708fb8d --- /dev/null +++ b/spec/plugin_helper.rb @@ -0,0 +1,6 @@ +SimpleCov.configure do + add_filter do |src| + src.filename !~ /discourse-custom-wizard/ || + src.filename =~ /spec/ + end +end \ No newline at end of file From e8c81765f08a7204621acca814053c94e087eda6 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 9 Dec 2019 17:16:29 +1100 Subject: [PATCH 8/9] Update .travis.yml --- .travis.yml | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/.travis.yml b/.travis.yml index b48ce894..9a695031 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,27 +1,12 @@ -# Uncomment tests runner when tests are added. - +# We want to use the KVM-based system, so require sudo sudo: required -#names: - #- docker +services: + - docker before_install: - - plugin_name=${PWD##*/} && echo $plugin_name - - chmod -R 777 . + - git clone --depth=1 https://github.com/discourse/discourse-plugin-ci -#script: - #- > - #docker run - #-e "COMMIT_HASH=origin/tests-passed" - #-e "SKIP_LINT=1" - #-e "RUBY_ONLY=1" - #-e SINGLE_PLUGIN=$plugin_name - #-v $(pwd):/var/www/discourse/plugins/$plugin_name - #discourse/discourse_test:release +install: true # Prevent travis doing bundle install -after_success: - - pip install virtualenv - - virtualenv ~/env - - source ~/env/bin/activate - - pip install transifex-client - - sudo echo $'[https://www.transifex.com]\nhostname = https://www.transifex.com\nusername = '"$TRANSIFEX_USER"$'\npassword = '"$TRANSIFEX_PASSWORD"$'\ntoken = '"$TRANSIFEX_API_TOKEN"$'\n' > ~/.transifexrc - - tx push -s +script: + - discourse-plugin-ci/script.sh From bc60283182fa6756412a22c0bb2864a9dd998616 Mon Sep 17 00:00:00 2001 From: Angus McLeod Date: Mon, 9 Dec 2019 19:14:31 +1100 Subject: [PATCH 9/9] require simplecov --- spec/plugin_helper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spec/plugin_helper.rb b/spec/plugin_helper.rb index f708fb8d..47368da5 100644 --- a/spec/plugin_helper.rb +++ b/spec/plugin_helper.rb @@ -1,3 +1,5 @@ +require 'simplecov' + SimpleCov.configure do add_filter do |src| src.filename !~ /discourse-custom-wizard/ ||