2017-04-20 04:46:28 +08:00
# encoding: utf-8
2019-04-30 08:27:42 +08:00
# frozen_string_literal: true
2017-04-20 04:46:28 +08:00
require 'rails_helper'
describe ThemeField do
2021-06-15 23:25:06 +08:00
after do
2018-03-05 08:04:23 +08:00
ThemeField . destroy_all
end
2018-08-08 12:46:34 +08:00
describe " scope: find_by_theme_ids " do
it " returns result in the specified order " do
theme = Fabricate ( :theme )
theme2 = Fabricate ( :theme )
theme3 = Fabricate ( :theme )
( 0 .. 1 ) . each do | num |
ThemeField . create! ( theme : theme , target_id : num , name : " header " , value : " <a>html</a> " )
ThemeField . create! ( theme : theme2 , target_id : num , name : " header " , value : " <a>html</a> " )
ThemeField . create! ( theme : theme3 , target_id : num , name : " header " , value : " <a>html</a> " )
end
expect ( ThemeField . find_by_theme_ids (
[ theme3 . id , theme . id , theme2 . id ]
) . pluck ( :theme_id ) ) . to eq (
[ theme3 . id , theme3 . id , theme . id , theme . id , theme2 . id , theme2 . id ]
)
end
end
2018-10-15 12:55:23 +08:00
it 'does not insert a script tag when there are no inline script' do
theme_field = ThemeField . create! ( theme_id : 1 , target_id : 0 , name : " body_tag " , value : '<div>new div</div>' )
2019-04-12 18:36:08 +08:00
theme_field . ensure_baked!
2018-10-15 12:55:23 +08:00
expect ( theme_field . value_baked ) . to_not include ( '<script' )
end
2019-11-12 22:30:19 +08:00
it 'adds an error when optimized image links are included' do
theme_field = ThemeField . create! ( theme_id : 1 , target_id : 0 , name : " body_tag " , value : << ~ HTML )
< img src = " http://mysite.invalid/uploads/default/optimized/1X/6d749a141f513f88f167e750e528515002043da1_2_1282x1000.png " / >
HTML
theme_field . ensure_baked!
expect ( theme_field . error ) . to include ( I18n . t ( " themes.errors.optimized_link " ) )
theme_field = ThemeField . create! ( theme_id : 1 , target_id : 0 , name : " scss " , value : << ~ SCSS )
body {
background : url ( http : / /m ysite . invalid / uploads / default / optimized / 1 X / 6 d749a141f513f88f167e750e528515002043da1_2_1282x1000 . png ) ;
}
SCSS
theme_field . ensure_baked!
expect ( theme_field . error ) . to include ( I18n . t ( " themes.errors.optimized_link " ) )
theme_field . update ( value : << ~ SCSS )
body {
background : url ( http : / /no tdiscourse . invalid / optimized / my_image . png ) ;
}
SCSS
theme_field . ensure_baked!
expect ( theme_field . error ) . to eq ( nil )
end
2018-10-15 12:55:23 +08:00
it 'only extracts inline javascript to an external file' do
2018-10-18 14:17:10 +08:00
html = << ~ HTML
2020-05-07 04:57:14 +08:00
< script type = " text/discourse-plugin " version = " 0.8 " >
var a = " inline discourse plugin " ;
< / script>
< script type = " text/template " data - template = " custom-template " >
< div > custom script type < / div>
< / script>
< script >
var b = " inline raw script " ;
< / script>
< script type = " texT/jAvasCripT " >
var c = " text/javascript " ;
< / script>
< script type = " application/javascript " >
var d = " application/javascript " ;
< / script>
< script src = " /external-script.js " > < / script>
2018-10-18 14:17:10 +08:00
HTML
2018-10-15 12:55:23 +08:00
theme_field = ThemeField . create! ( theme_id : 1 , target_id : 0 , name : " header " , value : html )
2019-04-12 18:36:08 +08:00
theme_field . ensure_baked!
2018-10-15 12:55:23 +08:00
expect ( theme_field . value_baked ) . to include ( " <script src= \" #{ theme_field . javascript_cache . url } \" ></script> " )
expect ( theme_field . value_baked ) . to include ( " external-script.js " )
2018-11-02 04:01:46 +08:00
expect ( theme_field . value_baked ) . to include ( '<script type="text/template"' )
expect ( theme_field . javascript_cache . content ) . to include ( 'a = "inline discourse plugin"' )
expect ( theme_field . javascript_cache . content ) . to include ( 'b = "inline raw script"' )
expect ( theme_field . javascript_cache . content ) . to include ( 'c = "text/javascript"' )
expect ( theme_field . javascript_cache . content ) . to include ( 'd = "application/javascript"' )
end
it 'adds newlines between the extracted javascripts' do
html = << ~ HTML
2020-05-07 04:57:14 +08:00
< script > var a = 10 < / script>
< script > var b = 10 < / script>
2018-11-02 04:01:46 +08:00
HTML
extracted = << ~ JavaScript
2020-05-07 04:57:14 +08:00
var a = 10
var b = 10
2018-11-02 04:01:46 +08:00
JavaScript
theme_field = ThemeField . create! ( theme_id : 1 , target_id : 0 , name : " header " , value : html )
2019-04-12 18:36:08 +08:00
theme_field . ensure_baked!
2019-01-17 19:46:11 +08:00
expect ( theme_field . javascript_cache . content ) . to include ( extracted )
2018-10-15 12:55:23 +08:00
end
it " correctly extracts and generates errors for transpiled js " do
2017-04-20 04:46:28 +08:00
html = <<HTML
< script type = " text/discourse-plugin " version = " 0.8 " >
badJavaScript ( ;
< / script>
HTML
2017-08-31 12:06:56 +08:00
2017-05-03 04:01:01 +08:00
field = ThemeField . create! ( theme_id : 1 , target_id : 0 , name : " header " , value : html )
2019-04-12 18:36:08 +08:00
field . ensure_baked!
2017-04-20 04:46:28 +08:00
expect ( field . error ) . not_to eq ( nil )
2018-10-15 12:55:23 +08:00
expect ( field . value_baked ) . to include ( " <script src= \" #{ field . javascript_cache . url } \" ></script> " )
expect ( field . javascript_cache . content ) . to include ( " Theme Transpilation Error: " )
2017-08-31 12:06:56 +08:00
field . update! ( value : '' )
2019-04-12 18:36:08 +08:00
field . ensure_baked!
2017-04-20 04:46:28 +08:00
expect ( field . error ) . to eq ( nil )
end
2018-04-03 17:53:00 +08:00
it " allows us to use theme settings in handlebars templates " do
html = <<HTML
< script type = 'text/x-handlebars' data - template - name = 'my-template' >
< div class = " testing-div " > { { themeSettings . string_setting } } < / div>
< / script>
HTML
2019-04-12 18:36:08 +08:00
ThemeField . create! ( theme_id : 1 , target_id : 3 , name : " yaml " , value : " string_setting: \" test text \\ \" 123! \" " ) . ensure_baked!
2018-10-15 12:55:23 +08:00
theme_field = ThemeField . create! ( theme_id : 1 , target_id : 0 , name : " head_tag " , value : html )
2019-04-12 18:36:08 +08:00
theme_field . ensure_baked!
2018-10-15 12:55:23 +08:00
javascript_cache = theme_field . javascript_cache
2018-04-03 17:53:00 +08:00
2018-10-15 12:55:23 +08:00
expect ( theme_field . value_baked ) . to include ( " <script src= \" #{ javascript_cache . url } \" ></script> " )
expect ( javascript_cache . content ) . to include ( " testing-div " )
expect ( javascript_cache . content ) . to include ( " string_setting " )
2019-01-17 19:46:11 +08:00
expect ( javascript_cache . content ) . to include ( " test text \\ \" 123! " )
2018-04-03 17:53:00 +08:00
end
2017-04-20 04:46:28 +08:00
it " correctly generates errors for transpiled css " do
css = " body { "
2017-05-03 04:01:01 +08:00
field = ThemeField . create! ( theme_id : 1 , target_id : 0 , name : " scss " , value : css )
2019-04-12 18:36:08 +08:00
field . ensure_baked!
2017-04-20 04:46:28 +08:00
expect ( field . error ) . not_to eq ( nil )
2021-02-03 02:09:41 +08:00
field . value = " @import 'missingfile'; "
2017-04-20 04:46:28 +08:00
field . save!
2019-04-12 18:36:08 +08:00
field . ensure_baked!
2021-03-13 00:17:42 +08:00
expect ( field . error ) . to include ( " File to import not found or unreadable: missingfile " )
2017-04-21 04:55:09 +08:00
2021-02-03 02:09:41 +08:00
field . value = " body {color: blue}; "
field . save!
field . ensure_baked!
2017-04-20 04:46:28 +08:00
expect ( field . error ) . to eq ( nil )
end
2017-05-11 02:43:05 +08:00
2019-04-12 18:36:08 +08:00
it " allows importing scss files " do
theme = Fabricate ( :theme )
2021-02-03 02:09:41 +08:00
main_field = theme . set_field ( target : :common , name : :scss , value : " .class1{color: red} \n @import 'rootfile1'; \n @import 'rootfile3'; " )
2019-04-12 18:36:08 +08:00
theme . set_field ( target : :extra_scss , name : " rootfile1 " , value : " .class2{color:green} \n @import 'foldername/subfile1'; " )
theme . set_field ( target : :extra_scss , name : " rootfile2 " , value : " .class3{color:green} " )
theme . set_field ( target : :extra_scss , name : " foldername/subfile1 " , value : " .class4{color:yellow} \n @import 'subfile2'; " )
theme . set_field ( target : :extra_scss , name : " foldername/subfile2 " , value : " .class5{color:yellow} \n @import '../rootfile2'; " )
2021-02-03 02:09:41 +08:00
theme . set_field ( target : :extra_scss , name : " rootfile3 " , value : " .class6{color:green} " )
2019-04-12 18:36:08 +08:00
theme . save!
result = main_field . compile_scss [ 0 ]
expect ( result ) . to include ( " .class1 " )
expect ( result ) . to include ( " .class2 " )
expect ( result ) . to include ( " .class3 " )
expect ( result ) . to include ( " .class4 " )
expect ( result ) . to include ( " .class5 " )
2021-02-03 02:09:41 +08:00
expect ( result ) . to include ( " .class6 " )
2019-04-12 18:36:08 +08:00
end
2019-06-03 17:41:00 +08:00
it " correctly handles extra JS fields " do
theme = Fabricate ( :theme )
2020-04-07 00:24:59 +08:00
js_field = theme . set_field ( target : :extra_js , name : " discourse/controllers/discovery.js.es6 " , value : " import 'discourse/lib/ajax'; console.log('hello from .js.es6'); " )
js_2_field = theme . set_field ( target : :extra_js , name : " discourse/controllers/discovery-2.js " , value : " import 'discourse/lib/ajax'; console.log('hello from .js'); " )
2019-06-03 17:41:00 +08:00
hbs_field = theme . set_field ( target : :extra_js , name : " discourse/templates/discovery.hbs " , value : " {{hello-world}} " )
2020-02-12 03:38:12 +08:00
raw_hbs_field = theme . set_field ( target : :extra_js , name : " discourse/templates/discovery.hbr " , value : " {{hello-world}} " )
hbr_field = theme . set_field ( target : :extra_js , name : " discourse/templates/other_discovery.hbr " , value : " {{hello-world}} " )
2019-06-03 17:41:00 +08:00
unknown_field = theme . set_field ( target : :extra_js , name : " discourse/controllers/discovery.blah " , value : " this wont work " )
theme . save!
2021-04-12 20:02:58 +08:00
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'); " )
2019-06-03 17:41:00 +08:00
2021-04-12 20:02:58 +08:00
expect ( hbs_field . reload . value_baked ) . to include ( 'Ember.TEMPLATES["javascripts/discovery"]' )
2020-05-06 00:15:03 +08:00
expect ( raw_hbs_field . reload . value_baked ) . to include ( 'addRawTemplate("discovery"' )
expect ( hbr_field . reload . value_baked ) . to include ( 'addRawTemplate("other_discovery"' )
2019-06-03 17:41:00 +08:00
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
2021-04-12 20:02:58 +08:00
expect ( theme . javascript_cache . content ) . to include ( 'Ember.TEMPLATES["javascripts/discovery"]' )
2020-05-06 00:15:03 +08:00
expect ( theme . javascript_cache . content ) . to include ( 'addRawTemplate("discovery"' )
2021-04-12 20:02:58 +08:00
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 \" " )
2019-06-03 17:41:00 +08:00
expect ( theme . javascript_cache . content ) . to include ( " var settings = " )
end
2017-12-19 23:10:44 +08:00
def create_upload_theme_field! ( name )
ThemeField . create! (
theme_id : 1 ,
target_id : 0 ,
value : " " ,
type_id : ThemeField . types [ :theme_upload_var ] ,
name : name ,
2019-04-12 18:36:08 +08:00
) . tap { | tf | tf . ensure_baked! }
2017-12-19 23:10:44 +08:00
end
it " ensures we don't use invalid SCSS variable names " do
expect { create_upload_theme_field! ( " 42 " ) } . to raise_error ( ActiveRecord :: RecordInvalid )
expect { create_upload_theme_field! ( " a42 " ) } . not_to raise_error
end
2018-03-05 08:04:23 +08:00
def get_fixture ( type )
File . read ( " #{ Rails . root } /spec/fixtures/theme_settings/ #{ type } _settings.yaml " )
end
def create_yaml_field ( value )
field = ThemeField . create! ( theme_id : 1 , target_id : Theme . targets [ :settings ] , name : " yaml " , value : value )
2019-04-12 18:36:08 +08:00
field . ensure_baked!
2018-03-05 08:04:23 +08:00
field
end
let ( :key ) { " themes.settings_errors " }
2019-03-08 22:49:06 +08:00
it " forces re-transpilation of theme JS when settings YAML changes " do
theme = Fabricate ( :theme )
settings_field = ThemeField . create! ( theme : theme , target_id : Theme . targets [ :settings ] , name : " yaml " , value : " setting: 5 " )
html = << ~ HTML
< script type = " text/discourse-plugin " version = " 0.8 " >
alert ( settings . setting ) ;
< / script>
HTML
js_field = ThemeField . create! ( theme : theme , target_id : ThemeField . types [ :html ] , name : " header " , value : html )
old_value_baked = js_field . value_baked
settings_field . update! ( value : " setting: 66 " )
js_field . reload
expect ( js_field . value_baked ) . to eq ( nil )
js_field . ensure_baked!
expect ( js_field . value_baked ) . to be_present
expect ( js_field . value_baked ) . not_to eq ( old_value_baked )
end
2018-03-05 08:04:23 +08:00
it " generates errors for bad YAML " do
yaml = " invalid_setting 5 "
field = create_yaml_field ( yaml )
expect ( field . error ) . to eq ( I18n . t ( " #{ key } .invalid_yaml " ) )
field . value = " valid_setting: true "
field . save!
2019-04-12 18:36:08 +08:00
field . ensure_baked!
2018-03-05 08:04:23 +08:00
expect ( field . error ) . to eq ( nil )
end
it " generates errors when default value's type doesn't match setting type " do
field = create_yaml_field ( get_fixture ( " invalid " ) )
expect ( field . error ) . to include ( I18n . t ( " #{ key } .default_not_match_type " , name : " no_match_setting " ) )
end
it " generates errors when no default value is passed " do
field = create_yaml_field ( get_fixture ( " invalid " ) )
expect ( field . error ) . to include ( I18n . t ( " #{ key } .default_value_missing " , name : " no_default_setting " ) )
end
it " generates errors when invalid type is passed " do
field = create_yaml_field ( get_fixture ( " invalid " ) )
expect ( field . error ) . to include ( I18n . t ( " #{ key } .data_type_not_a_number " , name : " invalid_type_setting " ) )
end
it " generates errors when default value is not within allowed range " do
field = create_yaml_field ( get_fixture ( " invalid " ) )
expect ( field . error ) . to include ( I18n . t ( " #{ key } .default_out_range " , name : " default_out_of_range " ) )
expect ( field . error ) . to include ( I18n . t ( " #{ key } .default_out_range " , name : " string_default_out_of_range " ) )
end
it " works correctly when valid yaml is provided " do
field = create_yaml_field ( get_fixture ( " valid " ) )
expect ( field . error ) . to be_nil
end
2019-01-17 19:46:11 +08:00
describe " locale fields " do
let! ( :theme ) { Fabricate ( :theme ) }
let! ( :theme2 ) { Fabricate ( :theme ) }
let! ( :theme3 ) { Fabricate ( :theme ) }
let! ( :en1 ) {
2020-05-07 04:57:14 +08:00
ThemeField . create! ( theme : theme , target_id : Theme . targets [ :translations ] , name : " en " ,
value : { en : { somestring1 : " helloworld " , group : { key1 : " enval1 " } } }
2019-01-17 19:46:11 +08:00
. deep_stringify_keys . to_yaml
)
}
let! ( :fr1 ) {
ThemeField . create! ( theme : theme , target_id : Theme . targets [ :translations ] , name : " fr " ,
value : { fr : { somestring1 : " bonjourworld " , group : { key2 : " frval2 " } } }
. deep_stringify_keys . to_yaml
)
}
let! ( :fr2 ) { ThemeField . create! ( theme : theme2 , target_id : Theme . targets [ :translations ] , name : " fr " , value : " " ) }
2020-05-07 04:57:14 +08:00
let! ( :en2 ) { ThemeField . create! ( theme : theme2 , target_id : Theme . targets [ :translations ] , name : " en " , value : " " ) }
2019-01-17 19:46:11 +08:00
let! ( :ca3 ) { ThemeField . create! ( theme : theme3 , target_id : Theme . targets [ :translations ] , name : " ca " , value : " " ) }
2020-05-07 04:57:14 +08:00
let! ( :en3 ) { ThemeField . create! ( theme : theme3 , target_id : Theme . targets [ :translations ] , name : " en " , value : " " ) }
2019-01-17 19:46:11 +08:00
describe " scopes " do
2019-02-26 22:22:02 +08:00
it " filter_locale_fields returns results in the correct order " do
expect ( ThemeField . find_by_theme_ids ( [ theme3 . id , theme . id , theme2 . id ] )
. filter_locale_fields (
2020-05-07 04:57:14 +08:00
[ " en " , " fr " ]
2019-01-17 19:46:11 +08:00
) ) . to eq ( [ en3 , en1 , fr1 , en2 , fr2 ] )
end
it " find_first_locale_fields returns only the first locale for each theme " do
expect ( ThemeField . find_first_locale_fields (
2020-05-07 04:57:14 +08:00
[ theme3 . id , theme . id , theme2 . id ] , [ " ca " , " en " , " fr " ]
2019-01-17 19:46:11 +08:00
) ) . to eq ( [ ca3 , en1 , en2 ] )
end
end
describe " # raw_translation_data " do
it " errors if the top level key is incorrect " do
fr1 . update ( value : { wrongkey : { somestring1 : " bonjourworld " } } . deep_stringify_keys . to_yaml )
expect { fr1 . raw_translation_data } . to raise_error ( ThemeTranslationParser :: InvalidYaml )
end
it " errors if there are multiple top level keys " do
fr1 . update ( value : { fr : { somestring1 : " bonjourworld " } , otherkey : " hello " } . deep_stringify_keys . to_yaml )
expect { fr1 . raw_translation_data } . to raise_error ( ThemeTranslationParser :: InvalidYaml )
end
it " errors if YAML includes arrays " do
fr1 . update ( value : { fr : [ " val1 " , " val2 " ] } . deep_stringify_keys . to_yaml )
expect { fr1 . raw_translation_data } . to raise_error ( ThemeTranslationParser :: InvalidYaml )
end
it " errors if YAML has invalid syntax " do
fr1 . update ( value : " fr: 'valuewithoutclosequote " )
expect { fr1 . raw_translation_data } . to raise_error ( ThemeTranslationParser :: InvalidYaml )
end
end
describe " # translation_data " do
it " loads correctly " do
expect ( fr1 . translation_data ) . to eq (
fr : { somestring1 : " bonjourworld " , group : { key2 : " frval2 " } } ,
2020-05-07 04:57:14 +08:00
en : { somestring1 : " helloworld " , group : { key1 : " enval1 " } }
2019-01-17 19:46:11 +08:00
)
end
it " raises errors for the current locale " do
fr1 . update ( value : { wrongkey : " hello " } . deep_stringify_keys . to_yaml )
expect { fr1 . translation_data } . to raise_error ( ThemeTranslationParser :: InvalidYaml )
end
it " doesn't raise errors for the fallback locale " do
en1 . update ( value : { wrongkey : " hello " } . deep_stringify_keys . to_yaml )
expect ( fr1 . translation_data ) . to eq (
fr : { somestring1 : " bonjourworld " , group : { key2 : " frval2 " } }
)
end
it " merges any overrides " do
# Overrides in the current locale (so in tests that will be english)
theme . update_translation ( " group.key1 " , " overriddentest1 " )
theme . reload
expect ( fr1 . translation_data ) . to eq (
fr : { somestring1 : " bonjourworld " , group : { key2 : " frval2 " } } ,
2020-05-07 04:57:14 +08:00
en : { somestring1 : " helloworld " , group : { key1 : " overriddentest1 " } }
2019-01-17 19:46:11 +08:00
)
end
end
describe " javascript cache " do
it " is generated correctly " do
fr1 . ensure_baked!
expect ( fr1 . value_baked ) . to include ( " <script src=' #{ fr1 . javascript_cache . url } '></script> " )
expect ( fr1 . javascript_cache . content ) . to include ( " bonjourworld " )
expect ( fr1 . javascript_cache . content ) . to include ( " helloworld " )
expect ( fr1 . javascript_cache . content ) . to include ( " enval1 " )
end
end
describe " prefix injection " do
it " injects into JS " do
html = << ~ HTML
2020-05-07 04:57:14 +08:00
< script type = " text/discourse-plugin " version = " 0.8 " >
var a = " inline discourse plugin " ;
< / script>
2019-01-17 19:46:11 +08:00
HTML
theme_field = ThemeField . create! ( theme_id : theme . id , target_id : 0 , name : " head_tag " , value : html )
2019-04-12 18:36:08 +08:00
theme_field . ensure_baked!
2019-01-17 19:46:11 +08:00
javascript_cache = theme_field . javascript_cache
expect ( javascript_cache . content ) . to include ( " inline discourse plugin " )
expect ( javascript_cache . content ) . to include ( " theme_translations. #{ theme . id } . " )
end
end
end
2020-11-25 07:49:12 +08:00
context " SVG sprite theme fields " do
let ( :upload ) { Fabricate ( :upload ) }
let ( :theme ) { Fabricate ( :theme ) }
let ( :theme_field ) { ThemeField . create! ( theme : theme , target_id : 0 , name : SvgSprite . theme_sprite_variable_name , upload : upload , value : " " , value_baked : " baked " , type_id : ThemeField . types [ :theme_upload_var ] ) }
it " is rebaked when upload changes " do
theme_field . update ( upload : Fabricate ( :upload ) )
expect ( theme_field . value_baked ) . to eq ( nil )
end
end
2017-04-20 04:46:28 +08:00
end