discourse/spec/lib/discourse_js_processor_spec.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

213 lines
7.2 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
require 'discourse_js_processor'
RSpec.describe DiscourseJsProcessor do
describe 'should_transpile?' do
it "returns false for empty strings" do
expect(DiscourseJsProcessor.should_transpile?(nil)).to eq(false)
expect(DiscourseJsProcessor.should_transpile?('')).to eq(false)
end
it "returns false for a regular js file" do
expect(DiscourseJsProcessor.should_transpile?("file.js")).to eq(false)
end
it "returns true for deprecated .es6 files" do
expect(DiscourseJsProcessor.should_transpile?("file.es6")).to eq(true)
expect(DiscourseJsProcessor.should_transpile?("file.js.es6")).to eq(true)
expect(DiscourseJsProcessor.should_transpile?("file.js.es6.erb")).to eq(true)
end
end
describe "skip_module?" do
it "returns false for empty strings" do
expect(DiscourseJsProcessor.skip_module?(nil)).to eq(false)
expect(DiscourseJsProcessor.skip_module?('')).to eq(false)
end
it "returns true if the header is present" do
expect(DiscourseJsProcessor.skip_module?("// cool comment\n// discourse-skip-module")).to eq(true)
end
it "returns false if the header is not present" do
expect(DiscourseJsProcessor.skip_module?("// just some JS\nconsole.log()")).to eq(false)
end
end
it "correctly transpiles widget hbs" do
result = DiscourseJsProcessor.transpile(<<~JS, "blah", "blah/mymodule")
import hbs from "discourse/widgets/hbs-compiler";
const template = hbs`{{somevalue}}`;
JS
expect(result).to eq <<~JS.strip
define("blah/mymodule", [], function () {
"use strict";
const template = function (attrs, state) {
var _r = [];
_r.push(somevalue);
return _r;
};
});
JS
end
it "correctly transpiles ember hbs" do
result = DiscourseJsProcessor.transpile(<<~JS, "blah", "blah/mymodule")
import { hbs } from 'ember-cli-htmlbars';
const template = hbs`{{somevalue}}`;
JS
expect(result).to eq <<~JS.strip
define("blah/mymodule", ["@ember/template-factory"], function (_templateFactory) {
"use strict";
const template = (0, _templateFactory.createTemplateFactory)(
/*
{{somevalue}}
*/
{
"id": null,
"block": "[[[1,[34,0]]],[],false,[\\"somevalue\\"]]",
"moduleName": "(unknown template module)",
"isStrictMode": false
});
});
JS
end
describe "Raw template theme transformations" do
# For the raw templates, we can easily render them serverside, so let's do that
let(:compiler) { DiscourseJsProcessor::Transpiler.new }
let(:theme_id) { 22 }
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}/app/assets/javascripts/node_modules/handlebars/dist/handlebars.js").read)
ctx.eval(helpers)
ctx
}
def render(template)
compiled = compiler.compile_raw_template(template, theme_id: theme_id)
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 "doesn't duplicate number parameter inside {{each}}" do
expect(compiler.compile_raw_template("{{#each item as |test test2|}}{{theme-setting 'setting_key'}}{{/each}}", theme_id: theme_id)).
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 "Ember template transformations" do
# For the Ember (Glimmer) templates, serverside rendering is not trivial,
# so we compile the expected result with the standard compiler and compare to the theme compiler
let(:theme_id) { 22 }
def theme_compile(template)
script = <<~JS
import { hbs } from 'ember-cli-htmlbars';
export default hbs(#{template.to_json});
JS
result = DiscourseJsProcessor.transpile(script, "", "theme/blah", theme_id: theme_id)
result.gsub(/\/\*(.*)\*\//m, "/* (js comment stripped) */")
end
def standard_compile(template)
script = <<~JS
import { hbs } from 'ember-cli-htmlbars';
export default hbs(#{template.to_json});
JS
result = DiscourseJsProcessor.transpile(script, "", "theme/blah")
result.gsub(/\/\*(.*)\*\//m, "/* (js comment stripped) */")
end
it 'adds the theme id to the helpers' do
expect(
theme_compile "{{theme-prefix 'translation_key'}}"
).to eq(
standard_compile "{{theme-prefix #{theme_id} 'translation_key'}}"
)
expect(
theme_compile "{{theme-i18n 'translation_key'}}"
).to eq(
standard_compile "{{theme-i18n #{theme_id} 'translation_key'}}"
)
expect(
theme_compile "{{theme-setting 'setting_key'}}"
).to eq(
standard_compile "{{theme-setting #{theme_id} 'setting_key'}}"
)
# Works when used inside other statements
expect(
theme_compile "{{dummy-helper (theme-prefix 'translation_key')}}"
).to eq(
standard_compile "{{dummy-helper (theme-prefix #{theme_id} 'translation_key')}}"
)
end
end
describe "Transpiler#terser" do
it "can minify code and provide sourcemaps" do
sources = {
"multiply.js" => "let multiply = (firstValue, secondValue) => firstValue * secondValue;",
"add.js" => "let add = (firstValue, secondValue) => firstValue + secondValue;"
}
result = DiscourseJsProcessor::Transpiler.new.terser(sources, { sourceMap: { includeSources: true } })
expect(result.keys).to contain_exactly("code", "decoded_map", "map")
begin
# Check the code still works
ctx = MiniRacer::Context.new
ctx.eval(result["code"])
expect(ctx.eval("multiply(2, 3)")).to eq(6)
expect(ctx.eval("add(2, 3)")).to eq(5)
ensure
ctx.dispose
end
map = JSON.parse(result["map"])
expect(map["sources"]).to contain_exactly(*sources.keys)
expect(map["sourcesContent"]).to contain_exactly(*sources.values)
end
end
end