From a53d8d3e61d33dde22628a8ceb94b6a27cb0a99a Mon Sep 17 00:00:00 2001 From: Osama Sayegh Date: Wed, 7 Apr 2021 10:39:57 +0300 Subject: [PATCH] FEATURE: Introduce theme/component QUnit tests (#12517) This commit allows themes and theme components to have QUnit tests. To add tests to your theme/component, create a top-level directory in your theme and name it `test`, and Discourse will save all the files in that directory (and its sub-directories) as "tests files" in the database. While tests files/directories are not required to be organized in a specific way, we recommend that you follow Discourse core's tests [structure](https://github.com/discourse/discourse/tree/master/app/assets/javascripts/discourse/tests). Writing theme tests should be identical to writing plugins or core tests; all the `import` statements and APIs that you see in core (or plugins) to define/setup tests should just work in themes. You do need a working Discourse install to run theme tests, and you have 2 ways to run theme tests: * In the browser at the `/qunit` route. `/qunit` will run tests of all active themes/components as well as core and plugins. The `/qunit` now accepts a `theme_name` or `theme_url` params that you can use to run tests of a specific theme/component like so: `/qunit?theme_name=`. * In the command line using the `themes:qunit` rake task. This take is meant to run tests of a single theme/component so you need to provide it with a theme name or URL like so: `bundle exec rake themes:qunit[name=]` or `bundle exec rake themes:qunit[url=]`. There are some refactors to internal code that's responsible for processing themes/components in Discourse, most notably: * `" + end + .join("\n") + .html_safe + end +end diff --git a/app/models/theme.rb b/app/models/theme.rb index 94972fb2b15..4803f1d4040 100644 --- a/app/models/theme.rb +++ b/app/models/theme.rb @@ -128,7 +128,7 @@ class Theme < ActiveRecord::Base SvgSprite.expire_cache end - BASE_COMPILER_VERSION = 17 + BASE_COMPILER_VERSION = 45 def self.compiler_version get_set_cache "compiler_version" do dependencies = [ @@ -262,11 +262,11 @@ class Theme < ActiveRecord::Base end end - def self.lookup_field(theme_ids, target, field) + def self.lookup_field(theme_ids, target, field, skip_transformation: false) return if theme_ids.blank? theme_ids = [theme_ids] unless Array === theme_ids - theme_ids = transform_ids(theme_ids) + theme_ids = transform_ids(theme_ids) if !skip_transformation cache_key = "#{theme_ids.join(",")}:#{target}:#{field}:#{Theme.compiler_version}" lookup = @cache[cache_key] return lookup.html_safe if lookup @@ -297,7 +297,7 @@ class Theme < ActiveRecord::Base end def self.targets - @targets ||= Enum.new(common: 0, desktop: 1, mobile: 2, settings: 3, translations: 4, extra_scss: 5, extra_js: 6) + @targets ||= Enum.new(common: 0, desktop: 1, mobile: 2, settings: 3, translations: 4, extra_scss: 5, extra_js: 6, tests_js: 7) end def self.lookup_target(target_id) diff --git a/app/models/theme_field.rb b/app/models/theme_field.rb index 673386b791b..038b9acfd9d 100644 --- a/app/models/theme_field.rb +++ b/app/models/theme_field.rb @@ -94,10 +94,38 @@ class ThemeField < ActiveRecord::Base node.remove end - doc.css('script[type="text/discourse-plugin"]').each do |node| - next unless node['version'].present? + doc.css('script[type="text/discourse-plugin"]').each_with_index do |node, index| + version = node['version'] + next if version.blank? + + initializer_name = "theme-field" + + "-#{self.id}" + + "-#{Theme.targets[self.target_id]}" + + "-#{ThemeField.types[self.type_id]}" + + "-script-#{index + 1}" begin - js_compiler.append_plugin_script(node.inner_html, node['version']) + js = <<~JS + import { withPluginApi } from "discourse/lib/plugin-api"; + import { rescueThemeError } from "discourse/lib/utilities"; + + const __theme_name__ = #{self.theme.name.to_s.inspect}; + export default { + name: #{initializer_name.inspect}, + after: "inject-objects", + + initialize() { + withPluginApi(#{version.inspect}, (api) => { + try { + #{node.inner_html} + } catch(err) { + rescueThemeError(__theme_name__, err, api); + } + }); + } + }; + JS + + js_compiler.append_module(js, "discourse/initializers/#{initializer_name}", include_variables: true) rescue ThemeJavascriptCompiler::CompileError => ex errors << ex.message end @@ -132,7 +160,7 @@ class ThemeField < ActiveRecord::Base begin case extension when "js.es6", "js" - js_compiler.append_module(content, filename) + js_compiler.append_module(content, filename, include_variables: true) when "hbs" js_compiler.append_ember_template(filename.sub("discourse/templates/", ""), content) when "hbr", "raw.hbs" @@ -285,6 +313,10 @@ class ThemeField < ActiveRecord::Base Theme.targets[self.target_id] == :extra_js end + def js_tests_field? + Theme.targets[self.target_id] == :tests_js + end + def basic_scss_field? ThemeField.basic_targets.include?(Theme.targets[self.target_id].to_s) && ThemeField.scss_fields.include?(self.name) @@ -315,7 +347,7 @@ class ThemeField < ActiveRecord::Base self.error = nil unless self.error.present? self.compiler_version = Theme.compiler_version DB.after_commit { CSP::Extension.clear_theme_extensions_cache! } - elsif extra_js_field? + elsif extra_js_field? || js_tests_field? self.value_baked, self.error = process_extra_js(self.value) self.error = nil unless self.error.present? self.compiler_version = Theme.compiler_version @@ -422,7 +454,7 @@ class ThemeField < ActiveRecord::Base hash = {} OPTIONS.each do |option| plural = :"#{option}s" - hash[option] = @allowed_values[plural][0] if @allowed_values[plural] && @allowed_values[plural].length == 1 + hash[option] = @allowed_values[plural][0] if @allowed_values[plural]&.length == 1 hash[option] = match[option] if hash[option].nil? end hash @@ -457,6 +489,9 @@ class ThemeField < ActiveRecord::Base ThemeFileMatcher.new(regex: /^javascripts\/(?.+)$/, targets: :extra_js, names: nil, types: :js, canonical: -> (h) { "javascripts/#{h[:name]}" }), + ThemeFileMatcher.new(regex: /^test\/(?.+)$/, + targets: :tests_js, names: nil, types: :js, + canonical: -> (h) { "test/#{h[:name]}" }), ThemeFileMatcher.new(regex: /^settings\.ya?ml$/, names: "yaml", types: :yaml, targets: :settings, canonical: -> (h) { "settings.yml" }), diff --git a/app/views/qunit/index.html.erb b/app/views/qunit/index.html.erb index 9b116c83dd5..a8b57ce32b8 100644 --- a/app/views/qunit/index.html.erb +++ b/app/views/qunit/index.html.erb @@ -5,6 +5,11 @@ <%= discourse_color_scheme_stylesheets %> <%= stylesheet_link_tag "test_helper" %> <%= javascript_include_tag "test_helper" %> + <%= theme_tests %> + <%= theme_translations_lookup %> + <%= theme_js_lookup %> + <%= theme_lookup("head_tag") %> + <%= javascript_include_tag "test_starter" %> <%= csrf_meta_tags %> diff --git a/config/routes.rb b/config/routes.rb index 9b1c0454936..3f5e4d644d1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -527,6 +527,7 @@ Discourse::Application.routes.draw do get "stylesheets/:name.css" => "stylesheets#show", constraints: { name: /[-a-z0-9_]+/ } get "color-scheme-stylesheet/:id(/:theme_id)" => "stylesheets#color_scheme", constraints: { format: :json } get "theme-javascripts/:digest.js" => "theme_javascripts#show", constraints: { digest: /\h{40}/ } + get "theme-javascripts/tests/:theme_id.js" => "theme_javascripts#show_tests" post "uploads/lookup-metadata" => "uploads#metadata" post "uploads" => "uploads#create" diff --git a/lib/tasks/qunit.rake b/lib/tasks/qunit.rake index 405e932cc64..1cb656993eb 100644 --- a/lib/tasks/qunit.rake +++ b/lib/tasks/qunit.rake @@ -61,7 +61,7 @@ task "qunit:test", [:timeout, :qunit_path] do |_, args| cmd = "node #{test_path}/run-qunit.js http://localhost:#{port}#{qunit_path}" options = { seed: (ENV["QUNIT_SEED"] || Random.new.seed), hidepassed: 1 } - %w{module filter qunit_skip_core qunit_single_plugin}.each do |arg| + %w{module filter qunit_skip_core qunit_single_plugin theme_name theme_url}.each do |arg| options[arg] = ENV[arg.upcase] if ENV[arg.upcase].present? end diff --git a/lib/tasks/themes.rake b/lib/tasks/themes.rake index 05674767466..e2912ec0577 100644 --- a/lib/tasks/themes.rake +++ b/lib/tasks/themes.rake @@ -96,3 +96,24 @@ task "themes:audit" => :environment do puts repo end end + +desc "Run QUnit tests of a theme/component" +task "themes:qunit", :theme_name_or_url do |t, args| + name_or_url = args[:theme_name_or_url] + if name_or_url.blank? + raise "A theme name or URL must be provided." + end + if name_or_url =~ /^(url|name)=(.+)/ + cmd = "THEME_#{Regexp.last_match(1).upcase}=#{Regexp.last_match(2)} " + cmd += `which rake`.strip + " qunit:test" + sh cmd + else + raise <<~MSG + Cannot parse passed argument #{name_or_url.inspect}. + Usage: + `bundle exec rake themes:unit[url=]` + OR + `bundle exec rake themes:unit[name=]` + MSG + end +end diff --git a/lib/theme_javascript_compiler.rb b/lib/theme_javascript_compiler.rb index 17bebf1b229..49178655c2a 100644 --- a/lib/theme_javascript_compiler.rb +++ b/lib/theme_javascript_compiler.rb @@ -151,6 +151,18 @@ class ThemeJavascriptCompiler class CompileError < StandardError end + def self.force_default_settings(content, theme) + settings_hash = {} + theme.settings.each do |setting| + settings_hash[setting.name] = setting.default + end + content.prepend <<~JS + (function() { + require("discourse/lib/theme-settings-store").registerSettings(#{theme.id}, #{settings_hash.to_json}, { force: true }); + })(); + JS + end + attr_accessor :content def initialize(theme_id, theme_name) @@ -162,10 +174,8 @@ class ThemeJavascriptCompiler def prepend_settings(settings_hash) @content.prepend <<~JS (function() { - if ('Discourse' in window && Discourse.__container__) { - Discourse.__container__ - .lookup("service:theme-settings") - .registerSettings(#{@theme_id}, #{settings_hash.to_json}); + if ('require' in window) { + require("discourse/lib/theme-settings-store").registerSettings(#{@theme_id}, #{settings_hash.to_json}); } })(); JS @@ -173,8 +183,10 @@ class ThemeJavascriptCompiler # TODO Error handling for handlebars templates def append_ember_template(name, hbs_template) + name = "javascripts/#{name}" if !name.start_with?("javascripts/") name = name.inspect compiled = EmberTemplatePrecompiler.new(@theme_id).compile(hbs_template) + # the `'Ember' in window` check is needed for no_ember pages content << <<~JS (function() { if ('Ember' in window) { @@ -204,18 +216,19 @@ class ThemeJavascriptCompiler raise CompileError.new e.instance_variable_get(:@error) # e.message contains the entire template, which could be very long end - def append_plugin_script(script, api_version) - @content << transpile(script, api_version) - end - def append_raw_script(script) @content << script + "\n" end def append_module(script, name, include_variables: true) - script = "#{theme_variables}#{script}" if include_variables + name = "discourse/theme-#{@theme_id}/#{name.gsub(/^discourse\//, '')}" + script = "#{theme_settings}#{script}" if include_variables transpiler = DiscourseJsProcessor::Transpiler.new - @content << transpiler.perform(script, "", name) + @content << <<~JS + if ('define' in window) { + #{transpiler.perform(script, "", name).strip} + } + JS rescue MiniRacer::RuntimeError => ex raise CompileError.new ex.message end @@ -226,11 +239,9 @@ class ThemeJavascriptCompiler private - def theme_variables + def theme_settings <<~JS - const __theme_name__ = "#{@theme_name.gsub('"', "\\\"")}"; - const settings = Discourse.__container__ - .lookup("service:theme-settings") + const settings = require("discourse/lib/theme-settings-store") .getObjectForTheme(#{@theme_id}); const themePrefix = (key) => `theme_translations.#{@theme_id}.${key}`; JS @@ -241,7 +252,8 @@ class ThemeJavascriptCompiler wrapped = <<~PLUGIN_API_JS (function() { if ('Discourse' in window && typeof Discourse._registerPluginCode === 'function') { - #{theme_variables} + const __theme_name__ = #{@theme_name.to_s.inspect}; + #{theme_settings} Discourse._registerPluginCode('#{version}', api => { try { #{es6_source} diff --git a/spec/models/remote_theme_spec.rb b/spec/models/remote_theme_spec.rb index 9bb82c9d5cb..6511842e4fa 100644 --- a/spec/models/remote_theme_spec.rb +++ b/spec/models/remote_theme_spec.rb @@ -40,6 +40,7 @@ describe RemoteTheme do "stylesheets/file.scss" => ".class1{color:red}", "stylesheets/empty.scss" => "", "javascripts/discourse/controllers/test.js.es6" => "console.log('test');", + "test/acceptance/theme-test.js" => "assert.ok(true);", "common/header.html" => "I AM HEADER", "common/random.html" => "I AM SILLY", "common/embedded.scss" => "EMBED", @@ -74,7 +75,7 @@ describe RemoteTheme do expect(@theme.theme_modifier_set.serialize_topic_excerpts).to eq(true) - expect(@theme.theme_fields.length).to eq(10) + expect(@theme.theme_fields.length).to eq(11) mapped = Hash[*@theme.theme_fields.map { |f| ["#{f.target_id}-#{f.name}", f.value] }.flatten] @@ -88,8 +89,9 @@ describe RemoteTheme do expect(mapped["3-yaml"]).to eq("boolean_setting: true") expect(mapped["4-en"]).to eq("sometranslations") + expect(mapped["7-acceptance/theme-test.js"]).to eq("assert.ok(true);") - expect(mapped.length).to eq(10) + expect(mapped.length).to eq(11) expect(@theme.settings.length).to eq(1) expect(@theme.settings.first.value).to eq(true) diff --git a/spec/models/theme_field_spec.rb b/spec/models/theme_field_spec.rb index 20e00e06940..b57a87d98fb 100644 --- a/spec/models/theme_field_spec.rb +++ b/spec/models/theme_field_spec.rb @@ -192,34 +192,22 @@ HTML unknown_field = theme.set_field(target: :extra_js, name: "discourse/controllers/discovery.blah", value: "this wont work") theme.save! - expected_js = <<~JS - define("discourse/controllers/discovery", ["discourse/lib/ajax"], function (_ajax) { - "use strict"; + js_field.reload + expect(js_field.value_baked).to include("if ('define' in window) {") + expect(js_field.value_baked).to include("define(\"discourse/theme-#{theme.id}/controllers/discovery\"") + expect(js_field.value_baked).to include("console.log('hello from .js.es6');") - var __theme_name__ = "#{theme.name}"; - - var settings = Discourse.__container__.lookup("service:theme-settings").getObjectForTheme(#{theme.id}); - - var themePrefix = function themePrefix(key) { - return "theme_translations.#{theme.id}.".concat(key); - }; - - console.log('hello from .js.es6'); - }); - JS - expect(js_field.reload.value_baked).to eq(expected_js.strip) - - expect(hbs_field.reload.value_baked).to include('Ember.TEMPLATES["discovery"]') + expect(hbs_field.reload.value_baked).to include('Ember.TEMPLATES["javascripts/discovery"]') expect(raw_hbs_field.reload.value_baked).to include('addRawTemplate("discovery"') expect(hbr_field.reload.value_baked).to include('addRawTemplate("other_discovery"') expect(unknown_field.reload.value_baked).to eq("") expect(unknown_field.reload.error).to eq(I18n.t("themes.compile_error.unrecognized_extension", extension: "blah")) # All together - expect(theme.javascript_cache.content).to include('Ember.TEMPLATES["discovery"]') + expect(theme.javascript_cache.content).to include('Ember.TEMPLATES["javascripts/discovery"]') expect(theme.javascript_cache.content).to include('addRawTemplate("discovery"') - expect(theme.javascript_cache.content).to include('define("discourse/controllers/discovery"') - expect(theme.javascript_cache.content).to include('define("discourse/controllers/discovery-2"') + expect(theme.javascript_cache.content).to include("define(\"discourse/theme-#{theme.id}/controllers/discovery\"") + expect(theme.javascript_cache.content).to include("define(\"discourse/theme-#{theme.id}/controllers/discovery-2\"") expect(theme.javascript_cache.content).to include("var settings =") end diff --git a/spec/models/theme_spec.rb b/spec/models/theme_spec.rb index ef359c139a3..7ae6a04caae 100644 --- a/spec/models/theme_spec.rb +++ b/spec/models/theme_spec.rb @@ -276,7 +276,7 @@ HTML def transpile(html) f = ThemeField.create!(target_id: Theme.targets[:mobile], theme_id: 1, name: "after_header", value: html) f.ensure_baked! - [f.value_baked, f.javascript_cache] + [f.value_baked, f.javascript_cache, f] end it "transpiles ES6 code" do @@ -286,10 +286,21 @@ HTML HTML - baked, javascript_cache = transpile(html) + baked, javascript_cache, field = transpile(html) expect(baked).to include(javascript_cache.url) - expect(javascript_cache.content).to include('var x = 1;') - expect(javascript_cache.content).to include("_registerPluginCode('0.1'") + + expect(javascript_cache.content).to include("if ('define' in window) {") + expect(javascript_cache.content).to include( + "define(\"discourse/theme-#{field.theme_id}/initializers/theme-field-#{field.id}-mobile-html-script-1\"" + ) + expect(javascript_cache.content).to include( + "settings = require(\"discourse/lib/theme-settings-store\").getObjectForTheme(#{field.theme_id});" + ) + expect(javascript_cache.content).to include("name: \"theme-field-#{field.id}-mobile-html-script-1\",") + expect(javascript_cache.content).to include("after: \"inject-objects\",") + expect(javascript_cache.content).to include("(0, _pluginApi.withPluginApi)(\"0.1\", function (api) {") + expect(javascript_cache.content).to include("var x = 1;") + expect(javascript_cache.content).to include("(0, _utilities.rescueThemeError)(__theme_name__, err, api);") end it "wraps constants calls in a readOnlyError function" do @@ -369,83 +380,32 @@ HTML theme_field = theme.set_field(target: :common, name: :after_header, value: '') theme.save! - transpiled = <<~HTML - (function() { - if ('Discourse' in window && Discourse.__container__) { - Discourse.__container__ - .lookup("service:theme-settings") - .registerSettings(#{theme.id}, {"name":"bob"}); - } - })(); - (function () { - if ('Discourse' in window && typeof Discourse._registerPluginCode === 'function') { - var __theme_name__ = "awesome theme\\\""; - - var settings = Discourse.__container__.lookup("service:theme-settings").getObjectForTheme(#{theme.id}); - - var themePrefix = function themePrefix(key) { - return "theme_translations.#{theme.id}.".concat(key); - }; - - Discourse._registerPluginCode('1.0', function (api) { - try { - alert(settings.name); - - var a = function a() {}; - } catch (err) { - var rescue = require("discourse/lib/utilities").rescueThemeError; - - rescue(__theme_name__, err, api); - } - }); - } - })(); - HTML - theme_field.reload expect(Theme.lookup_field(theme.id, :desktop, :after_header)).to include(theme_field.javascript_cache.url) - expect(theme_field.javascript_cache.content).to eq(transpiled.strip) + expect(theme_field.javascript_cache.content).to include("if ('require' in window) {") + expect(theme_field.javascript_cache.content).to include( + "require(\"discourse/lib/theme-settings-store\").registerSettings(#{theme_field.theme.id}, {\"name\":\"bob\"});" + ) + expect(theme_field.javascript_cache.content).to include("if ('define' in window) {") + expect(theme_field.javascript_cache.content).to include( + "define(\"discourse/theme-#{theme_field.theme.id}/initializers/theme-field-#{theme_field.id}-common-html-script-1\"," + ) + expect(theme_field.javascript_cache.content).to include("var __theme_name__ = \"awesome theme\\\"\";") + expect(theme_field.javascript_cache.content).to include("name: \"theme-field-#{theme_field.id}-common-html-script-1\",") + expect(theme_field.javascript_cache.content).to include("after: \"inject-objects\",") + expect(theme_field.javascript_cache.content).to include("(0, _pluginApi.withPluginApi)(\"1.0\", function (api)") + expect(theme_field.javascript_cache.content).to include("alert(settings.name)") + expect(theme_field.javascript_cache.content).to include("var a = function a() {}") setting = theme.settings.find { |s| s.name == :name } setting.value = 'bill' theme.save! - transpiled = <<~HTML - (function() { - if ('Discourse' in window && Discourse.__container__) { - Discourse.__container__ - .lookup("service:theme-settings") - .registerSettings(#{theme.id}, {"name":"bill"}); - } - })(); - (function () { - if ('Discourse' in window && typeof Discourse._registerPluginCode === 'function') { - var __theme_name__ = "awesome theme\\\""; - - var settings = Discourse.__container__.lookup("service:theme-settings").getObjectForTheme(#{theme.id}); - - var themePrefix = function themePrefix(key) { - return "theme_translations.#{theme.id}.".concat(key); - }; - - Discourse._registerPluginCode('1.0', function (api) { - try { - alert(settings.name); - - var a = function a() {}; - } catch (err) { - var rescue = require("discourse/lib/utilities").rescueThemeError; - - rescue(__theme_name__, err, api); - } - }); - } - })(); - HTML - theme_field.reload + expect(theme_field.javascript_cache.content).to include( + "require(\"discourse/lib/theme-settings-store\").registerSettings(#{theme_field.theme.id}, {\"name\":\"bill\"});" + ) expect(Theme.lookup_field(theme.id, :desktop, :after_header)).to include(theme_field.javascript_cache.url) - expect(theme_field.javascript_cache.content).to eq(transpiled.strip) end it 'is empty when the settings are invalid' do diff --git a/spec/requests/qunit_controller_spec.rb b/spec/requests/qunit_controller_spec.rb new file mode 100644 index 00000000000..b0a9535dc2c --- /dev/null +++ b/spec/requests/qunit_controller_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe QunitController do + let(:theme) { Fabricate(:theme, name: 'main-theme') } + let(:component) { Fabricate(:theme, component: true, name: 'enabled-component') } + let(:disabled_component) { Fabricate(:theme, component: true, enabled: false, name: 'disabled-component') } + + before do + Theme.destroy_all + theme.set_default! + component.add_relative_theme!(:parent, theme) + disabled_component.add_relative_theme!(:parent, theme) + [theme, component, disabled_component].each do |t| + t.set_field( + target: :extra_js, + type: :js, + name: "discourse/initializers/my-#{t.id}-initializer.js", + value: "console.log(#{t.id});" + ) + t.set_field( + target: :tests_js, + type: :js, + name: "acceptance/some-test-#{t.id}.js", + value: "assert.ok(#{t.id});" + ) + t.save! + end + end + + context "when no theme is specified" do + it "includes tests of enabled theme + components" do + get '/qunit' + js_urls = JavascriptCache.where(theme_id: [theme.id, component.id]).map(&:url) + expect(js_urls.size).to eq(2) + js_urls.each do |url| + expect(response.body).to include(url) + end + [theme, component].each do |t| + expect(response.body).to include("/theme-javascripts/tests/#{t.id}.js") + end + + js_urls = JavascriptCache.where(theme_id: disabled_component).map(&:url) + expect(js_urls.size).to eq(1) + js_urls.each do |url| + expect(response.body).not_to include(url) + end + expect(response.body).not_to include("/theme-javascripts/tests/#{disabled_component.id}.js") + end + end + + context "when a theme is specified" do + it "includes tests of the specified theme only" do + [theme, disabled_component].each do |t| + get "/qunit?theme_name=#{t.name}" + js_urls = JavascriptCache.where(theme_id: t.id).map(&:url) + expect(js_urls.size).to eq(1) + js_urls.each do |url| + expect(response.body).to include(url) + end + expect(response.body).to include("/theme-javascripts/tests/#{t.id}.js") + + excluded = Theme.pluck(:id) - [t.id] + js_urls = JavascriptCache.where(theme_id: excluded).map(&:url) + expect(js_urls.size).to eq(2) + js_urls.each do |url| + expect(response.body).not_to include(url) + end + excluded.each do |id| + expect(response.body).not_to include("/theme-javascripts/tests/#{id}.js") + end + end + end + end +end diff --git a/spec/requests/theme_javascripts_controller_spec.rb b/spec/requests/theme_javascripts_controller_spec.rb index 6c05efd4e57..b63d9efbbd2 100644 --- a/spec/requests/theme_javascripts_controller_spec.rb +++ b/spec/requests/theme_javascripts_controller_spec.rb @@ -54,4 +54,24 @@ describe ThemeJavascriptsController do end end end + + describe "#show_tests" do + context "theme settings" do + let(:component) { Fabricate(:theme, component: true, name: 'enabled-component') } + + it "forces default values" do + ThemeField.create!( + theme: component, + target_id: Theme.targets[:settings], + name: "yaml", + value: "num_setting: 5" + ) + component.reload + component.update_setting(:num_setting, 643) + + get "/theme-javascripts/tests/#{component.id}.js" + expect(response.body).to include("require(\"discourse/lib/theme-settings-store\").registerSettings(#{component.id}, {\"num_setting\":5}, { force: true });") + end + end + end end