2017-04-12 22:52:52 +08:00
require 'rails_helper'
describe Theme do
before do
Theme . clear_cache!
end
let :user do
Fabricate ( :user )
end
2017-09-04 19:27:58 +08:00
let ( :guardian ) do
Guardian . new ( user )
end
2017-04-12 22:52:52 +08:00
let :customization_params do
2017-07-28 09:20:09 +08:00
{ name : 'my name' , user_id : user . id , header : " my awesome header " }
2017-04-12 22:52:52 +08:00
end
let :customization do
2018-08-08 12:46:34 +08:00
Fabricate ( :theme , customization_params )
2017-04-12 22:52:52 +08:00
end
2018-08-08 12:46:34 +08:00
let ( :theme ) { Fabricate ( :theme , user : user ) }
2018-08-24 09:30:00 +08:00
let ( :child ) { Fabricate ( :theme , user : user , component : true ) }
2017-05-05 02:03:07 +08:00
it 'can properly clean up color schemes' do
scheme = ColorScheme . create! ( theme_id : theme . id , name : 'test' )
scheme2 = ColorScheme . create! ( theme_id : theme . id , name : 'test2' )
2018-08-08 12:46:34 +08:00
Fabricate ( :theme , color_scheme_id : scheme2 . id )
2017-05-05 02:03:07 +08:00
theme . destroy!
scheme2 . reload
expect ( scheme2 ) . not_to eq ( nil )
expect ( scheme2 . theme_id ) . to eq ( nil )
expect ( ColorScheme . find_by ( id : scheme . id ) ) . to eq ( nil )
end
2017-04-12 22:52:52 +08:00
it 'can support child themes' do
2017-05-03 04:01:01 +08:00
child . set_field ( target : :common , name : " header " , value : " World " )
child . set_field ( target : :desktop , name : " header " , value : " Desktop " )
child . set_field ( target : :mobile , name : " header " , value : " Mobile " )
2017-04-12 22:52:52 +08:00
child . save!
2018-07-12 12:18:21 +08:00
expect ( Theme . lookup_field ( child . id , :desktop , " header " ) ) . to eq ( " World \n Desktop " )
expect ( Theme . lookup_field ( child . id , " mobile " , :header ) ) . to eq ( " World \n Mobile " )
2017-04-12 22:52:52 +08:00
2017-05-03 04:01:01 +08:00
child . set_field ( target : :common , name : " header " , value : " Worldie " )
2017-04-12 22:52:52 +08:00
child . save!
2018-07-12 12:18:21 +08:00
expect ( Theme . lookup_field ( child . id , :mobile , :header ) ) . to eq ( " Worldie \n Mobile " )
2017-04-12 22:52:52 +08:00
2018-08-08 12:46:34 +08:00
parent = Fabricate ( :theme , user : user )
2017-04-12 22:52:52 +08:00
2017-05-03 04:01:01 +08:00
parent . set_field ( target : :common , name : " header " , value : " Common Parent " )
parent . set_field ( target : :mobile , name : " header " , value : " Mobile Parent " )
2017-04-12 22:52:52 +08:00
parent . save!
parent . add_child_theme! ( child )
2018-07-12 12:18:21 +08:00
expect ( Theme . lookup_field ( parent . id , :mobile , " header " ) ) . to eq ( " Common Parent \n Mobile Parent \n Worldie \n Mobile " )
2017-04-12 22:52:52 +08:00
end
it 'can correctly find parent themes' do
2018-08-08 12:46:34 +08:00
theme . add_child_theme! ( child )
expect ( child . dependant_themes . length ) . to eq ( 1 )
end
it " doesn't allow multi-level theme components " do
grandchild = Fabricate ( :theme , user : user )
grandparent = Fabricate ( :theme , user : user )
2017-04-12 22:52:52 +08:00
2018-08-08 12:46:34 +08:00
expect do
child . add_child_theme! ( grandchild )
end . to raise_error ( Discourse :: InvalidParameters , I18n . t ( " themes.errors.no_multilevels_components " ) )
expect do
grandparent . add_child_theme! ( theme )
end . to raise_error ( Discourse :: InvalidParameters , I18n . t ( " themes.errors.no_multilevels_components " ) )
end
2017-04-12 22:52:52 +08:00
2018-08-08 12:46:34 +08:00
it " doesn't allow a child to be user selectable " do
child . update ( user_selectable : true )
expect ( child . errors . full_messages ) . to contain_exactly ( I18n . t ( " themes.errors.component_no_user_selectable " ) )
end
it " doesn't allow a child to be set as the default theme " do
expect do
child . set_default!
end . to raise_error ( Discourse :: InvalidParameters , I18n . t ( " themes.errors.component_no_default " ) )
2017-04-12 22:52:52 +08:00
end
2018-08-24 09:30:00 +08:00
it " doesn't allow a component to have color scheme " do
scheme = ColorScheme . create! ( name : " test " )
child . update ( color_scheme : scheme )
expect ( child . errors . full_messages ) . to contain_exactly ( I18n . t ( " themes.errors.component_no_color_scheme " ) )
end
2017-04-12 22:52:52 +08:00
it 'should correct bad html in body_tag_baked and head_tag_baked' do
2017-05-03 04:01:01 +08:00
theme . set_field ( target : :common , name : " head_tag " , value : " <b>I am bold " )
2017-04-12 22:52:52 +08:00
theme . save!
2018-07-12 12:18:21 +08:00
expect ( Theme . lookup_field ( theme . id , :desktop , " head_tag " ) ) . to eq ( " <b>I am bold</b> " )
2017-04-12 22:52:52 +08:00
end
it 'should precompile fragments in body and head tags' do
with_template = <<HTML
< script type = 'text/x-handlebars' name = 'template' >
{ { hello } }
< / script>
< script type = 'text/x-handlebars' data - template - name = 'raw_template.raw' >
{ { hello } }
< / script>
HTML
2017-05-03 04:01:01 +08:00
theme . set_field ( target : :common , name : " header " , value : with_template )
2017-04-12 22:52:52 +08:00
theme . save!
2018-07-12 12:18:21 +08:00
baked = Theme . lookup_field ( theme . id , :mobile , " header " )
2017-04-12 22:52:52 +08:00
expect ( baked ) . to match ( / HTMLBars / )
expect ( baked ) . to match ( / raw-handlebars / )
end
it 'should create body_tag_baked on demand if needed' do
2017-05-03 04:01:01 +08:00
theme . set_field ( target : :common , name : :body_tag , value : " <b>test " )
2017-04-12 22:52:52 +08:00
theme . save
ThemeField . update_all ( value_baked : nil )
2018-07-12 12:18:21 +08:00
expect ( Theme . lookup_field ( theme . id , :desktop , :body_tag ) ) . to match ( / <b>test< \/ b> / )
2017-04-12 22:52:52 +08:00
end
2018-08-08 12:46:34 +08:00
it 'can find fields for multiple themes' do
theme2 = Fabricate ( :theme )
theme . set_field ( target : :common , name : :body_tag , value : " <b>testtheme1</b> " )
theme2 . set_field ( target : :common , name : :body_tag , value : " <b>theme2test</b> " )
theme . save!
theme2 . save!
field = Theme . lookup_field ( [ theme . id , theme2 . id ] , :desktop , :body_tag )
expect ( field ) . to match ( / <b>testtheme1< \/ b> / )
expect ( field ) . to match ( / <b>theme2test< \/ b> / )
end
2018-08-24 09:30:00 +08:00
describe " # switch_to_component! " do
it " correctly converts a theme to component " do
theme . add_child_theme! ( child )
scheme = ColorScheme . create! ( name : 'test' )
theme . update! ( color_scheme_id : scheme . id , user_selectable : true )
theme . set_default!
theme . switch_to_component!
theme . reload
expect ( theme . component ) . to eq ( true )
expect ( theme . user_selectable ) . to eq ( false )
expect ( theme . default? ) . to eq ( false )
expect ( theme . color_scheme_id ) . to eq ( nil )
expect ( ChildTheme . where ( parent_theme : theme ) . exists? ) . to eq ( false )
end
end
describe " # switch_to_theme! " do
it " correctly converts a component to theme " do
theme . add_child_theme! ( child )
child . switch_to_theme!
theme . reload
child . reload
expect ( child . component ) . to eq ( false )
expect ( ChildTheme . where ( child_theme : child ) . exists? ) . to eq ( false )
end
end
2018-08-08 12:46:34 +08:00
describe " .transform_ids " do
2018-08-24 09:30:00 +08:00
let! ( :child ) { Fabricate ( :theme , component : true ) }
let! ( :child2 ) { Fabricate ( :theme , component : true ) }
2018-08-08 12:46:34 +08:00
2018-08-24 09:30:00 +08:00
before do
2018-08-08 12:46:34 +08:00
theme . add_child_theme! ( child )
theme . add_child_theme! ( child2 )
2018-08-24 09:30:00 +08:00
end
it " adds the child themes of the parent " do
sorted = [ child . id , child2 . id ] . sort
2018-08-10 08:38:36 +08:00
expect ( Theme . transform_ids ( [ theme . id ] ) ) . to eq ( [ theme . id , * sorted ] )
fake_id = [ child . id , child2 . id , theme . id ] . min - 5
fake_id2 = [ child . id , child2 . id , theme . id ] . max + 5
expect ( Theme . transform_ids ( [ theme . id , fake_id2 , fake_id ] ) )
. to eq ( [ theme . id , fake_id , * sorted , fake_id2 ] )
2018-08-08 12:46:34 +08:00
end
it " doesn't insert children when extend is false " do
2018-08-10 08:38:36 +08:00
fake_id = theme . id + 1
fake_id2 = fake_id + 2
fake_id3 = fake_id2 + 3
2018-08-08 12:46:34 +08:00
expect ( Theme . transform_ids ( [ theme . id ] , extend : false ) ) . to eq ( [ theme . id ] )
2018-08-10 08:38:36 +08:00
expect ( Theme . transform_ids ( [ theme . id , fake_id3 , fake_id , fake_id2 , fake_id2 ] , extend : false ) )
. to eq ( [ theme . id , fake_id , fake_id2 , fake_id3 ] )
2018-08-08 12:46:34 +08:00
end
end
2017-04-12 22:52:52 +08:00
context " plugin api " do
def transpile ( html )
2018-03-05 08:04:23 +08:00
f = ThemeField . create! ( target_id : Theme . targets [ :mobile ] , theme_id : 1 , name : " after_header " , value : html )
2017-04-12 22:52:52 +08:00
f . value_baked
end
it " transpiles ES6 code " do
html = <<HTML
< script type = 'text/discourse-plugin' version = '0.1' >
const x = 1 ;
< / script>
HTML
transpiled = transpile ( html )
expect ( transpiled ) . to match ( / \ <script \ > / )
expect ( transpiled ) . to match ( / var x = 1; / )
expect ( transpiled ) . to match ( / _registerPluginCode \ ('0.1' / )
end
it " converts errors to a script type that is not evaluated " do
html = <<HTML
< script type = 'text/discourse-plugin' version = '0.1' >
const x = 1 ;
x = 2 ;
< / script>
HTML
transpiled = transpile ( html )
expect ( transpiled ) . to match ( / text \/ discourse-js-error / )
expect ( transpiled ) . to match ( / read-only / )
end
end
2017-05-03 04:01:01 +08:00
context 'theme vars' do
2017-11-14 12:22:59 +08:00
it 'works in parent theme' do
theme . set_field ( target : :common , name : :scss , value : 'body {color: $magic; }' )
theme . set_field ( target : :common , name : :magic , value : 'red' , type : :theme_var )
theme . set_field ( target : :common , name : :not_red , value : 'red' , type : :theme_var )
2018-08-24 09:30:00 +08:00
theme . component = true
2017-11-14 12:22:59 +08:00
theme . save
2018-08-08 12:46:34 +08:00
parent_theme = Fabricate ( :theme )
2017-11-14 12:22:59 +08:00
parent_theme . set_field ( target : :common , name : :scss , value : 'body {background-color: $not_red; }' )
parent_theme . set_field ( target : :common , name : :not_red , value : 'blue' , type : :theme_var )
parent_theme . save
parent_theme . add_child_theme! ( theme )
scss , _map = Stylesheet :: Compiler . compile ( '@import "theme_variables"; @import "desktop_theme"; ' , " theme.scss " , theme_id : parent_theme . id )
expect ( scss ) . to include ( " color:red " )
expect ( scss ) . to include ( " background-color:blue " )
end
2017-05-03 04:01:01 +08:00
it 'can generate scss based off theme vars' do
theme . set_field ( target : :common , name : :scss , value : 'body {color: $magic; content: quote($content)}' )
theme . set_field ( target : :common , name : :magic , value : 'red' , type : :theme_var )
theme . set_field ( target : :common , name : :content , value : 'Sam\'s Test' , type : :theme_var )
theme . save
2017-07-28 09:20:09 +08:00
scss , _map = Stylesheet :: Compiler . compile ( '@import "theme_variables"; @import "desktop_theme"; ' , " theme.scss " , theme_id : theme . id )
2017-05-03 04:01:01 +08:00
expect ( scss ) . to include ( " red " )
expect ( scss ) . to include ( '"Sam\'s Test"' )
end
2017-05-08 23:38:48 +08:00
let :image do
file_from_fixtures ( " logo.png " )
end
it 'can handle uploads based of ThemeField' do
2017-05-11 06:16:57 +08:00
upload = UploadCreator . new ( image , " logo.png " ) . create_for ( - 1 )
2017-05-08 23:38:48 +08:00
theme . set_field ( target : :common , name : :logo , upload_id : upload . id , type : :theme_upload_var )
theme . set_field ( target : :common , name : :scss , value : 'body {background-image: url($logo)}' )
theme . save!
# make sure we do not nuke it
freeze_time ( SiteSetting . clean_orphan_uploads_grace_period_hours + 1 ) . hours . from_now
Jobs :: CleanUpUploads . new . execute ( nil )
expect ( Upload . where ( id : upload . id ) ) . to be_exist
2017-05-10 05:20:28 +08:00
# no error for theme field
theme . reload
expect ( theme . theme_fields . find_by ( name : :scss ) . error ) . to eq ( nil )
2017-05-08 23:38:48 +08:00
2017-07-28 09:20:09 +08:00
scss , _map = Stylesheet :: Compiler . compile ( '@import "theme_variables"; @import "desktop_theme"; ' , " theme.scss " , theme_id : theme . id )
2017-05-08 23:38:48 +08:00
expect ( scss ) . to include ( upload . url )
end
2017-05-03 04:01:01 +08:00
end
2018-03-05 08:04:23 +08:00
context " theme settings " do
2018-03-05 12:35:41 +08:00
it " allows values to be used in scss " do
2018-03-05 08:04:23 +08:00
theme . set_field ( target : :settings , name : :yaml , value : " background_color: red \n font_size: 25px " )
theme . set_field ( target : :common , name : :scss , value : 'body {background-color: $background_color; font-size: $font-size}' )
theme . save!
scss , _map = Stylesheet :: Compiler . compile ( '@import "theme_variables"; @import "desktop_theme"; ' , " theme.scss " , theme_id : theme . id )
expect ( scss ) . to include ( " background-color:red " )
expect ( scss ) . to include ( " font-size:25px " )
2018-03-05 12:35:41 +08:00
setting = theme . settings . find { | s | s . name == :font_size }
setting . value = '30px'
scss , _map = Stylesheet :: Compiler . compile ( '@import "theme_variables"; @import "desktop_theme"; ' , " theme.scss " , theme_id : theme . id )
expect ( scss ) . to include ( " font-size:30px " )
2018-03-05 08:04:23 +08:00
end
2018-03-05 12:35:41 +08:00
it " allows values to be used in JS " do
theme . set_field ( target : :settings , name : :yaml , value : " name: bob " )
theme . set_field ( target : :common , name : :after_header , value : '<script type="text/discourse-plugin" version="1.0">alert(settings.name); let a = ()=>{};</script>' )
theme . save!
transpiled = << ~ HTML
2018-07-18 13:13:47 +08:00
< script > if ( 'Discourse' in window ) {
Discourse . _registerPluginCode ( '1.0' , function ( api ) {
var settings = { " name " : " bob " } ;
alert ( settings . name ) ; var a = function a ( ) { } ;
} ) ;
} < / script>
2018-03-05 12:35:41 +08:00
HTML
2018-07-12 12:18:21 +08:00
expect ( Theme . lookup_field ( theme . id , :desktop , :after_header ) ) . to eq ( transpiled . strip )
2018-03-05 12:35:41 +08:00
setting = theme . settings . find { | s | s . name == :name }
setting . value = 'bill'
transpiled = << ~ HTML
2018-07-18 13:13:47 +08:00
< script > if ( 'Discourse' in window ) {
Discourse . _registerPluginCode ( '1.0' , function ( api ) {
var settings = { " name " : " bill " } ;
alert ( settings . name ) ; var a = function a ( ) { } ;
} ) ;
} < / script>
2018-03-05 12:35:41 +08:00
HTML
2018-07-12 12:18:21 +08:00
expect ( Theme . lookup_field ( theme . id , :desktop , :after_header ) ) . to eq ( transpiled . strip )
2018-03-05 12:35:41 +08:00
end
2018-03-05 08:04:23 +08:00
end
2018-07-12 12:18:21 +08:00
it 'correctly caches theme ids' do
2017-05-03 23:31:16 +08:00
Theme . destroy_all
2018-08-08 12:46:34 +08:00
theme
theme2 = Fabricate ( :theme )
2017-04-15 01:35:12 +08:00
2018-08-08 12:46:34 +08:00
expect ( Theme . theme_ids ) . to contain_exactly ( theme . id , theme2 . id )
expect ( Theme . user_theme_ids ) . to eq ( [ ] )
2017-04-15 01:35:12 +08:00
2018-08-08 12:46:34 +08:00
theme . update! ( user_selectable : true )
2017-04-15 01:35:12 +08:00
2018-08-08 12:46:34 +08:00
expect ( Theme . user_theme_ids ) . to contain_exactly ( theme . id )
2017-04-15 01:35:12 +08:00
2018-08-08 12:46:34 +08:00
theme2 . update! ( user_selectable : true )
expect ( Theme . user_theme_ids ) . to contain_exactly ( theme . id , theme2 . id )
theme . update! ( user_selectable : false )
theme2 . update! ( user_selectable : false )
2017-04-15 01:35:12 +08:00
theme . set_default!
2018-08-08 12:46:34 +08:00
expect ( Theme . user_theme_ids ) . to contain_exactly ( theme . id )
2017-04-15 01:35:12 +08:00
theme . destroy
2018-08-08 12:46:34 +08:00
theme2 . destroy
2017-04-15 01:35:12 +08:00
2018-08-08 12:46:34 +08:00
expect ( Theme . theme_ids ) . to eq ( [ ] )
expect ( Theme . user_theme_ids ) . to eq ( [ ] )
2017-04-15 01:35:12 +08:00
end
2017-09-04 19:27:58 +08:00
it 'correctly caches user_themes template' do
Theme . destroy_all
json = Site . json_for ( guardian )
user_themes = JSON . parse ( json ) [ " user_themes " ]
expect ( user_themes ) . to eq ( [ ] )
2018-08-08 12:46:34 +08:00
theme = Fabricate ( :theme , name : " bob " , user_selectable : true )
2017-09-04 19:27:58 +08:00
json = Site . json_for ( guardian )
user_themes = JSON . parse ( json ) [ " user_themes " ] . map { | t | t [ " name " ] }
expect ( user_themes ) . to eq ( [ " bob " ] )
theme . name = " sam "
theme . save!
json = Site . json_for ( guardian )
user_themes = JSON . parse ( json ) [ " user_themes " ] . map { | t | t [ " name " ] }
expect ( user_themes ) . to eq ( [ " sam " ] )
Theme . destroy_all
json = Site . json_for ( guardian )
user_themes = JSON . parse ( json ) [ " user_themes " ]
expect ( user_themes ) . to eq ( [ ] )
end
2018-07-12 12:18:21 +08:00
def cached_settings ( id )
Theme . find_by ( id : id ) . included_settings . to_json
2018-03-05 08:04:23 +08:00
end
it 'handles settings cache correctly' do
Theme . destroy_all
2018-07-12 12:18:21 +08:00
expect ( cached_settings ( theme . id ) ) . to eq ( " {} " )
2018-03-05 08:04:23 +08:00
theme . set_field ( target : :settings , name : " yaml " , value : " boolean_setting: true " )
theme . save!
2018-07-12 12:18:21 +08:00
expect ( cached_settings ( theme . id ) ) . to match ( / \ "boolean_setting \ ":true / )
2018-03-05 08:04:23 +08:00
theme . settings . first . value = " false "
2018-07-12 12:18:21 +08:00
expect ( cached_settings ( theme . id ) ) . to match ( / \ "boolean_setting \ ":false / )
2018-03-05 08:04:23 +08:00
child . set_field ( target : :settings , name : " yaml " , value : " integer_setting: 54 " )
child . save!
theme . add_child_theme! ( child )
2018-07-12 12:18:21 +08:00
json = cached_settings ( theme . id )
2018-03-05 08:04:23 +08:00
expect ( json ) . to match ( / \ "boolean_setting \ ":false / )
expect ( json ) . to match ( / \ "integer_setting \ ":54 / )
2018-07-12 12:18:21 +08:00
expect ( cached_settings ( child . id ) ) . to eq ( " { \" integer_setting \" :54} " )
2018-03-05 08:04:23 +08:00
child . destroy!
2018-07-12 12:18:21 +08:00
json = cached_settings ( theme . id )
2018-03-05 08:04:23 +08:00
expect ( json ) . not_to match ( / \ "integer_setting \ ":54 / )
expect ( json ) . to match ( / \ "boolean_setting \ ":false / )
end
2017-04-12 22:52:52 +08:00
end