diff --git a/src/panels/main.js b/src/panels/main.js index d808fa6d1..20c5e6006 100644 --- a/src/panels/main.js +++ b/src/panels/main.js @@ -11,7 +11,7 @@ * ``` * * - * Before using methods you should get first the module from the GrapesJs instance, in this way: + * Before using methods you should get first the module from the editor instance, in this way: * * ```js * var panelService = editor.get('Panels'); @@ -19,6 +19,22 @@ * * @module Panels * @param {Object} config Configurations + * @param {Array} [config.defaults=[]] Array of possible panels + * @example + * ... + * panels: { + * defaults: [{ + * id: 'main-toolbar', + * buttons: [{ + * id: 'btn-id', + * className: 'some', + * attributes: { + * title: 'MyTitle' + * } + * }], + * }], + * } + * ... */ define(function(require) { @@ -26,6 +42,7 @@ define(function(require) { var c = config || {}, defaults = require('./config/config'), + Panel = require('./model/Panel'), Panels = require('./model/Panels'), PanelsView = require('./view/PanelsView'); @@ -137,6 +154,8 @@ define(function(require) { }); }, + Panel: Panel, + }; }; diff --git a/test/runner/main.js b/test/runner/main.js index b7ed5ace2..e455cd5ed 100644 --- a/test/runner/main.js +++ b/test/runner/main.js @@ -15,6 +15,7 @@ require(['../src/config/require-config.js', 'config/config.js'], function() { 'specs/class_manager/main.js', 'specs/css_composer/main.js', 'specs/code_manager/main.js', + 'specs/panels/main.js', ], function(chai) { var should = chai.should(), diff --git a/test/specs/panels/e2e/CssComposer.js b/test/specs/panels/e2e/CssComposer.js new file mode 100644 index 000000000..5727060a8 --- /dev/null +++ b/test/specs/panels/e2e/CssComposer.js @@ -0,0 +1,82 @@ + +define(function(require) { + + return { + run : function(){ + describe('E2E tests', function() { + + before(function () { + this.$fixtures = $("#fixtures"); + this.$fixture = $('
'); + }); + + beforeEach(function () { + this.Grapes = require('editor/main'); + this.gjs = new this.Grapes({ + stylePrefix: '', + storageType: 'none', + storageManager: { storageType: 'none', }, + assetManager: { storageType: 'none', }, + container: 'csscomposer-fixture', + }); + this.cssc = this.gjs.editor.get('CssComposer'); + this.clsm = this.gjs.editor.get('ClassManager'); + this.$fixture.empty().appendTo(this.$fixtures); + this.gjs.render(); + this.rulesSet = [ + { selectors: [{name: 'test1'}, {name: 'test2'}] }, + { selectors: [{name: 'test2'}, {name: 'test3'}] }, + { selectors: [{name: 'test3'}] } + ]; + }); + + afterEach(function () { + delete this.Grapes; + delete this.gjs; + delete this.cssc; + delete this.clsm; + }); + + after(function () { + this.$fixture.remove(); + }); + + it('Rules are correctly imported from default property', function() { + var gj = new this.Grapes({ + stylePrefix: '', + storageType: 'none', + storageManager: { storageType: 'none', }, + assetManager: { storageType: 'none', }, + cssComposer: { defaults: this.rulesSet}, + container: 'csscomposer-fixture', + }); + var cssc = gj.editor.get('CssComposer'); + cssc.getRules().length.should.equal(this.rulesSet.length); + var cls = gj.editor.get('ClassManager').getClasses(); + cls.length.should.equal(3); + }); + + + it('New rule adds correctly the class inside classe manager', function() { + var rules = this.cssc.getRules(); + rules.add({ selectors: [{name: 'test1'}] }); + this.clsm.getClasses().at(0).get('name').should.equal('test1'); + }); + + it('New rules are correctly imported inside classe manager', function() { + var rules = this.cssc.getRules(); + this.rulesSet.forEach(function(item){ + rules.add(item); + }); + var cls = this.clsm.getClasses(); + cls.length.should.equal(3); + cls.at(0).get('name').should.equal('test1'); + cls.at(1).get('name').should.equal('test2'); + cls.at(2).get('name').should.equal('test3'); + }); + + }); + } + }; + +}); \ No newline at end of file diff --git a/test/specs/panels/main.js b/test/specs/panels/main.js new file mode 100644 index 000000000..ea467d68b --- /dev/null +++ b/test/specs/panels/main.js @@ -0,0 +1,101 @@ +var modulePath = './../../../test/specs/panels'; + +define([ + 'Panels' + //modulePath + '/model/CssModels', + ], + function( + Panels + ) { + + describe('Panels', function() { + + describe('Main', function() { + + var obj; + + beforeEach(function () { + obj = new Panels(); + }); + + afterEach(function () { + delete obj; + }); + + it('Object exists', function() { + obj.should.be.exist; + }); + + it("No panels inside", function() { + obj.getPanels().length.should.equal(0); + }); + + it("Adds new panel correctly via object", function() { + var panel = obj.addPanel({id: 'test'}); + panel.get('id').should.equal('test'); + }); + + it("New panel has no buttons", function() { + var panel = obj.addPanel({id: 'test'}); + panel.get('buttons').length.should.equal(0); + }); + + it("Adds new panel correctly via Panel instance", function() { + var oPanel = new obj.Panel({id: 'test'}); + var panel = obj.addPanel(oPanel); + panel.should.deep.equal(oPanel); + panel.get('id').should.equal('test'); + }); + + it("getPanel returns null in case there is no requested panel", function() { + (obj.getPanel('test') == null).should.equal(true); + }); + + it("getPanel returns correctly the panel", function() { + var panel = obj.addPanel({id: 'test'}); + obj.getPanel('test').should.deep.equal(panel); + }); + + it("Can't add button to non existent panel", function() { + (obj.addButton('test', {id:'btn'}) == null).should.equal(true); + }); + + it("Add button correctly", function() { + var panel = obj.addPanel({id: 'test'}); + var btn = obj.addButton('test', {id:'btn'}); + panel.get('buttons').length.should.equal(1); + panel.get('buttons').at(0).get('id').should.equal('btn'); + }); + + it("getButton returns null in case there is no requested panel", function() { + (obj.getButton('test', 'btn') == null).should.equal(true); + }); + + it("getButton returns null in case there is no requested button", function() { + var panel = obj.addPanel({id: 'test'}); + (obj.getButton('test', 'btn') == null).should.equal(true); + }); + + it("getButton returns correctly the button", function() { + var panel = obj.addPanel({id: 'test'}); + var btn = obj.addButton('test', {id:'btn'}); + obj.getButton('test', 'btn').should.deep.equal(btn); + }); + + it("Renders correctly", function() { + obj.render().should.be.ok; + }); + + it("Active correctly activable buttons", function() { + var spy = sinon.spy(); + var panel = obj.addPanel({id: 'test'}); + var btn = obj.addButton('test', {id:'btn', active: true}); + btn.on('updateActive', spy); + obj.active(); + spy.called.should.equal(true); + }); + + }); + + }); +}); \ No newline at end of file diff --git a/test/specs/panels/model/CssModels.js b/test/specs/panels/model/CssModels.js new file mode 100644 index 000000000..da0cd5c80 --- /dev/null +++ b/test/specs/panels/model/CssModels.js @@ -0,0 +1,84 @@ +var path = 'CssComposer/model/'; +define([path + 'CssRule', + path + 'CssRules', + path + 'Selectors', + 'ClassManager/model/ClassTag'], + function(CssRule, CssRules, Selectors, ClassTag) { + + return { + run : function(){ + describe('CssRule', function() { + + beforeEach(function () { + this.obj = new CssRule(); + }); + + afterEach(function () { + delete this.obj; + }); + + it('Has selectors property', function() { + this.obj.has('selectors').should.equal(true); + }); + + it('Has style property', function() { + this.obj.has('style').should.equal(true); + }); + + it('Has state property', function() { + this.obj.has('state').should.equal(true); + }); + + it('No default selectors', function() { + this.obj.get('selectors').length.should.equal(0); + }); + + it('Compare returns true with the same selectors', function() { + var s1 = this.obj.get('selectors').add({ name: 'test1' }); + var s2 = this.obj.get('selectors').add({ name: 'test2' }); + this.obj.compare([s1, s2]).should.equal(true); + }); + + it('Compare with different state', function() { + var s1 = this.obj.get('selectors').add({ name: 'test1' }); + var s2 = this.obj.get('selectors').add({ name: 'test2' }); + this.obj.set('state','hover'); + this.obj.compare([s1, s2]).should.equal(false); + this.obj.compare([s1, s2], 'hover').should.equal(true); + }); + + it('Compare with different maxWidth', function() { + var s1 = this.obj.get('selectors').add({ name: 'test1' }); + var s2 = this.obj.get('selectors').add({ name: 'test2' }); + this.obj.set('state','hover'); + this.obj.set('maxWidth','1000'); + this.obj.compare([s1, s2]).should.equal(false); + this.obj.compare([s1, s2], 'hover').should.equal(false); + this.obj.compare([s2, s1], 'hover', '1000').should.equal(true); + }); + + }); + + describe('CssRules', function() { + + it('Creates collection item correctly', function() { + var c = new CssRules(); + var m = c.add({}); + m.should.be.an.instanceOf(CssRule); + }); + + }); + + describe('Selectors', function() { + + it('Creates collection item correctly', function() { + var c = new Selectors(); + var m = c.add({}); + m.should.be.an.instanceOf(ClassTag); + }); + + }); + } + }; + +}); \ No newline at end of file diff --git a/test/specs/panels/view/CssRuleView.js b/test/specs/panels/view/CssRuleView.js new file mode 100644 index 000000000..9e91748bf --- /dev/null +++ b/test/specs/panels/view/CssRuleView.js @@ -0,0 +1,114 @@ +var path = 'CssComposer/view/'; +define([path + 'CssRuleView', 'CssComposer/model/CssRule'], + function(CssRuleView, CssRule) { + + return { + run : function(){ + describe('CssRuleView', function() { + + before(function () { + this.$fixtures = $("#fixtures"); + this.$fixture = $('
'); + }); + + beforeEach(function () { + var m = new CssRule(); + this.view = new CssRuleView({ + model: m + }); + this.$fixture.empty().appendTo(this.$fixtures); + this.$fixture.html(this.view.render().el); + }); + + afterEach(function () { + this.view.model.destroy(); + }); + + after(function () { + this.$fixture.remove(); + }); + + it('Object exists', function() { + CssRuleView.should.be.exist; + }); + + it('Correct behaviour of renderSelectors with single selector', function() { + this.view.model.get('selectors').add({name: 'test'}); + this.view.renderSelectors().should.equal('.test'); + }); + + it('Correct behaviour of renderSelectors with multiple selectors', function() { + this.view.model.get('selectors').add([{name: 'test2'}, {name: 'test1'}]); + this.view.renderSelectors().should.equal('.test2.test1'); + }); + + it('Correct behaviour of renderProperties with single property', function() { + this.view.model.set('style', {'prop': 'value'}); + this.view.renderProperties().should.equal('prop:value;'); + }); + + it('Correct behaviour of renderProperties with multiple properties', function() { + this.view.model.set('style', {'prop2': 'value2', 'prop3': 'value3'}); + this.view.renderProperties().should.equal('prop2:value2;prop3:value3;'); + }); + + it('Empty style inside', function() { + this.$fixture.html().should.equal(''); + }); + + it('On update of style always empty as there is no selectors', function() { + this.view.model.set('style', {'prop':'value'}); + this.$fixture.html().should.equal(''); + }); + + describe('CssRuleView with selectors', function() { + + beforeEach(function () { + var m = new CssRule({ + selectors: [{name:'test1'}, {name:'test2'}] + }); + this.regView = new CssRuleView({ + model: m + }); + this.regView.render(); + }); + + afterEach(function () { + this.regView.model.destroy(); + }); + + it('Empty with no style', function() { + this.regView.$el.html().should.equal(''); + }); + + it('Not empty on update of style', function() { + this.regView.model.set('style', {'prop':'value'}); + this.regView.$el.html().should.equal('.test1.test2{prop:value;}'); + }); + + it('State correctly rendered', function() { + this.regView.model.set('style', {'prop':'value'}); + this.regView.model.set('state', 'hover'); + this.regView.$el.html().should.equal('.test1.test2:hover{prop:value;}'); + }); + + it('State render changes on update', function() { + this.regView.model.set('style', {'prop':'value'}); + this.regView.model.set('state', 'hover'); + this.regView.model.set('state', ''); + this.regView.$el.html().should.equal('.test1.test2{prop:value;}'); + }); + + it('Empty on clear', function() { + this.regView.model.set('style', {'prop':'value'}); + this.regView.model.set('style', {}); + this.regView.$el.html().should.equal(''); + }); + + }); + + }); + } + }; + +}); \ No newline at end of file diff --git a/test/specs/panels/view/CssRulesView.js b/test/specs/panels/view/CssRulesView.js new file mode 100644 index 000000000..3bcd33d41 --- /dev/null +++ b/test/specs/panels/view/CssRulesView.js @@ -0,0 +1,54 @@ +var path = 'CssComposer/view/'; +define([path + 'CssRulesView', 'CssComposer/model/CssRules'], + function(CssRulesView, CssRules) { + + return { + run : function(){ + describe('CssRulesView', function() { + + before(function () { + this.$fixtures = $("#fixtures"); + this.$fixture = $('
'); + }); + + beforeEach(function () { + var col = new CssRules([]); + this.view = new CssRulesView({ + collection: col + }); + this.$fixture.empty().appendTo(this.$fixtures); + this.$fixture.html(this.view.render().el); + }); + + afterEach(function () { + this.view.collection.reset(); + }); + + after(function () { + this.$fixture.remove(); + }); + + it('Object exists', function() { + CssRulesView.should.be.exist; + }); + + it("Collection is empty", function (){ + this.view.$el.html().should.be.empty; + }); + + it("Add new rule", function (){ + sinon.stub(this.view, "addToCollection"); + this.view.collection.add({}); + this.view.addToCollection.calledOnce.should.equal(true); + }); + + it("Render new rule", function (){ + this.view.collection.add({}); + this.view.$el.html().should.not.be.empty; + }); + + }); + } + }; + +}); \ No newline at end of file