mirror of
https://github.com/discourse/discourse.git
synced 2024-12-11 22:34:04 +08:00
cd24eff5d9
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=<your_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=<theme_name>]` or `bundle exec rake themes:qunit[url=<theme_url>]`. There are some refactors to how Discourse processes JavaScript that comes with themes/components, and these refactors may break your JS customizations; see https://meta.discourse.org/t/upcoming-core-changes-that-may-break-some-themes-components-april-12/186252?u=osama for details on how you can check if your themes/components are affected and what you need to do to fix them. This commit also improves theme error handling in Discourse. We will now be able to catch errors that occur when theme initializers are run and prevent them from breaking the site and other themes/components.
145 lines
5.7 KiB
Ruby
145 lines
5.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'rails_helper'
|
|
|
|
describe ThemeJavascriptCompiler do
|
|
|
|
let(:theme_id) { 22 }
|
|
|
|
describe ThemeJavascriptCompiler::RawTemplatePrecompiler do
|
|
# For the raw templates, we can easily render them serverside, so let's do that
|
|
|
|
let(:compiler) { described_class.new(theme_id) }
|
|
|
|
let(:helpers) {
|
|
<<~JS
|
|
Handlebars.registerHelper('theme-prefix', function(themeId, string) {
|
|
return `theme_translations.${themeId}.${string}`
|
|
})
|
|
Handlebars.registerHelper('theme-i18n', function(themeId, string) {
|
|
return `translated(theme_translations.${themeId}.${string})`
|
|
})
|
|
Handlebars.registerHelper('theme-setting', function(themeId, string) {
|
|
return `setting(${themeId}:${string})`
|
|
})
|
|
Handlebars.registerHelper('dummy-helper', function(string) {
|
|
return `dummy(${string})`
|
|
})
|
|
JS
|
|
}
|
|
|
|
let(:mini_racer) {
|
|
ctx = MiniRacer::Context.new
|
|
ctx.eval(File.open("#{Rails.root}/vendor/assets/javascripts/handlebars.js").read)
|
|
ctx.eval(helpers)
|
|
ctx
|
|
}
|
|
|
|
def render(template)
|
|
compiled = compiler.compile(template)
|
|
mini_racer.eval "Handlebars.template(#{compiled.squish})({})"
|
|
end
|
|
|
|
it 'adds the theme id to the helpers' do
|
|
# Works normally
|
|
expect(render("{{theme-prefix 'translation_key'}}")).
|
|
to eq('theme_translations.22.translation_key')
|
|
expect(render("{{theme-i18n 'translation_key'}}")).
|
|
to eq('translated(theme_translations.22.translation_key)')
|
|
expect(render("{{theme-setting 'setting_key'}}")).
|
|
to eq('setting(22:setting_key)')
|
|
|
|
# Works when used inside other statements
|
|
expect(render("{{dummy-helper (theme-prefix 'translation_key')}}")).
|
|
to eq('dummy(theme_translations.22.translation_key)')
|
|
end
|
|
|
|
it 'works with the old settings syntax' do
|
|
expect(render("{{themeSettings.setting_key}}")).
|
|
to eq('setting(22:setting_key)')
|
|
|
|
# Works when used inside other statements
|
|
expect(render("{{dummy-helper themeSettings.setting_key}}")).
|
|
to eq('dummy(setting(22:setting_key))')
|
|
end
|
|
|
|
it "doesn't duplicate number parameter inside {{each}}" do
|
|
expect(compiler.compile("{{#each item as |test test2|}}{{theme-setting 'setting_key'}}{{/each}}")).
|
|
to include('{"name":"theme-setting","hash":{},"hashTypes":{},"hashContexts":{},"types":["NumberLiteral","StringLiteral"]')
|
|
# Fail would be if theme-setting is defined with types:["NumberLiteral","NumberLiteral","StringLiteral"]
|
|
end
|
|
end
|
|
|
|
describe ThemeJavascriptCompiler::EmberTemplatePrecompiler do
|
|
# For the Ember (Glimmer) templates, serverside rendering is not trivial,
|
|
# so check the compiled JSON against known working output
|
|
let(:compiler) { described_class.new(theme_id) }
|
|
let(:helper_opcode) do
|
|
append = statement("{{dummy-helper 1}}")[0]
|
|
helper = append[1]
|
|
helper[0]
|
|
end
|
|
|
|
def statement(template)
|
|
compiled = compiler.compile(template)
|
|
data = JSON.parse(compiled)
|
|
block = JSON.parse(data["block"])
|
|
block["statements"]
|
|
end
|
|
|
|
it 'adds the theme id to the helpers' do
|
|
expect(statement("{{theme-prefix 'translation_key'}}")).
|
|
to eq([[1, [helper_opcode, "theme-prefix", [22, "translation_key"], nil], false]])
|
|
expect(statement("{{theme-i18n 'translation_key'}}")).
|
|
to eq([[1, [helper_opcode, "theme-i18n", [22, "translation_key"], nil], false]])
|
|
expect(statement("{{theme-setting 'setting_key'}}")).
|
|
to eq([[1, [helper_opcode, "theme-setting", [22, "setting_key"], nil], false]])
|
|
|
|
# Works when used inside other statements
|
|
expect(statement("{{dummy-helper (theme-prefix 'translation_key')}}")).
|
|
to eq([[1, [helper_opcode, "dummy-helper", [[helper_opcode, "theme-prefix", [22, "translation_key"], nil]], nil], false]])
|
|
end
|
|
|
|
it 'works with the old settings syntax' do
|
|
expect(statement("{{themeSettings.setting_key}}")).
|
|
to eq([[1, [helper_opcode, "theme-setting", [22, "setting_key"], [["deprecated"], [true]]], false]])
|
|
|
|
# Works when used inside other statements
|
|
expect(statement("{{dummy-helper themeSettings.setting_key}}")).
|
|
to eq([[1, [helper_opcode, "dummy-helper", [[helper_opcode, "theme-setting", [22, "setting_key"], [["deprecated"], [true]]]], nil], false]])
|
|
end
|
|
end
|
|
|
|
describe "#append_raw_template" do
|
|
let(:compiler) { ThemeJavascriptCompiler.new(1, 'marks') }
|
|
it 'uses the correct template paths' do
|
|
template = "<h1>hello</h1>"
|
|
name = "/path/to/templates1"
|
|
compiler.append_raw_template("#{name}.raw", template)
|
|
expect(compiler.content.to_s).to include("addRawTemplate(\"#{name}\"")
|
|
|
|
name = "/path/to/templates2"
|
|
compiler.append_raw_template("#{name}.hbr", template)
|
|
expect(compiler.content.to_s).to include("addRawTemplate(\"#{name}\"")
|
|
|
|
name = "/path/to/templates3"
|
|
compiler.append_raw_template("#{name}.hbs", template)
|
|
expect(compiler.content.to_s).to include("addRawTemplate(\"#{name}.hbs\"")
|
|
end
|
|
end
|
|
|
|
describe "#append_ember_template" do
|
|
let(:compiler) { ThemeJavascriptCompiler.new(1, 'marks') }
|
|
it 'prepends `javascripts/` to template name if it is not prepended' do
|
|
compiler.append_ember_template("/connectors/blah-1", "{{var}}")
|
|
expect(compiler.content.to_s).to include('Ember.TEMPLATES["javascripts/connectors/blah-1"]')
|
|
|
|
compiler.append_ember_template("connectors/blah-2", "{{var}}")
|
|
expect(compiler.content.to_s).to include('Ember.TEMPLATES["javascripts/connectors/blah-2"]')
|
|
|
|
compiler.append_ember_template("javascripts/connectors/blah-3", "{{var}}")
|
|
expect(compiler.content.to_s).to include('Ember.TEMPLATES["javascripts/connectors/blah-3"]')
|
|
end
|
|
end
|
|
end
|