jsharmony-cms
Version:
1,193 lines (1,139 loc) • 77.3 kB
JavaScript
/*
Copyright 2021 apHarmony
This file is part of jsHarmony.
jsHarmony is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
jsHarmony is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this package. If not, see <http://www.gnu.org/licenses/>.
*/
var assert = require('assert');
var jsHarmonyCMS = require('../jsHarmonyCMS.js');
var _ = require('lodash');
var ejs = require('ejs');
var jsh = new jsHarmonyCMS.Application();
var jshcms = jsh.Modules['jsHarmonyCMS'];
var funcs = jshcms.funcs;
var componentPrefix = '<% locals.renderTemplate = locals.renderTemplate.bind(null, locals); function getEJSOutput(f){ var pos = __output.length; f(); return __output.substr(pos); } %>';
describe('HTMLDoc Attributes', function() {
it('Append New Attributes', function(done) {
var htdoc = new funcs.HTMLDoc('<html><body first="abc"></body></html>');
htdoc.applyNodes([
{ //Apply properties
pred: function(node){ return htdoc.isTag(node, 'body'); },
exec: function(node){
htdoc.removeAttr(node, 'first');
htdoc.appendAttr(node, 'second', 'def', ' ');
htdoc.appendAttr(node, 'second', 'ghi', ' ');
htdoc.appendAttr(node, 'third', '123', ' ');
htdoc.appendAttr(node, 'third', '456', ' ');
}
},
]);
htdoc.trimRemoved();
assert.equal(htdoc.content,'<html><body third="123 456" second="def ghi"></body></html>');
done();
});
it('Append New Attributes to Single Tag', function(done) {
var htdoc = new funcs.HTMLDoc('<html><body/></html>');
htdoc.applyNodes([
{ //Apply properties
pred: function(node){ return htdoc.isTag(node, 'body'); },
exec: function(node){
htdoc.appendAttr(node, 'second', 'def', ' ');
htdoc.appendAttr(node, 'second', 'ghi', ' ');
htdoc.appendAttr(node, 'third', '123', ' ');
htdoc.appendAttr(node, 'third', '456', ' ');
}
},
]);
assert.equal(htdoc.content,'<html><body third="123 456" second="def ghi"/></html>');
done();
});
it('Update Existing Attributes and New', function(done) {
var htdoc = new funcs.HTMLDoc('<html><body first="abc" fourth="iii"></body></html>');
htdoc.applyNodes([
{ //Apply properties
pred: function(node){ return htdoc.isTag(node, 'body'); },
exec: function(node){
htdoc.appendAttr(node, 'first', 'zzz', ' ');
htdoc.appendAttr(node, 'first', 'yyy', ' ');
htdoc.appendAttr(node, 'second', 'def', ' ');
htdoc.appendAttr(node, 'second', 'ghi', ' ');
htdoc.appendAttr(node, 'third', '123', ' ');
htdoc.appendAttr(node, 'third', '456', ' ');
htdoc.appendAttr(node, 'fourth', 'jjj', ' ');
htdoc.appendAttr(node, 'fourth', 'lll', ' ');
}
},
]);
assert.equal(htdoc.content,'<html><body third="123 456" second="def ghi" first="abc zzz yyy" fourth="iii jjj lll"></body></html>');
done();
});
it('Remove Attribute then Add', function(done) {
var htdoc = new funcs.HTMLDoc('<html><body first="abc"></body></html>');
htdoc.applyNodes([
{ //Apply properties
pred: function(node){ return htdoc.isTag(node, 'body'); },
exec: function(node){
htdoc.removeAttr(node, 'first');
htdoc.appendAttr(node, 'first', 'def', ' ');
htdoc.appendAttr(node, 'first', 'ghi', ' ');
htdoc.appendAttr(node, 'third', '123', ' ');
htdoc.appendAttr(node, 'third', '456', ' ');
}
},
]);
htdoc.trimRemoved();
assert.equal(htdoc.content,'<html><body third="123 456" first="def ghi"></body></html>');
done();
});
it('Append and Add Attributes', function(done) {
var htdoc = new funcs.HTMLDoc('<html><body first="abc" second="aaa"></body></html>');
htdoc.applyNodes([
{ //Apply properties
pred: function(node){ return htdoc.isTag(node, 'body'); },
exec: function(node){
htdoc.removeAttr(node, 'second');
htdoc.appendAttr(node, 'first', 'def', ' ');
htdoc.appendAttr(node, 'first', 'ghi', ' ');
htdoc.appendAttr(node, 'third', '123', ' ');
htdoc.appendAttr(node, 'third', '456', ' ');
}
},
]);
htdoc.trimRemoved();
assert.equal(htdoc.content,'<html><body third="123 456" first="abc def ghi"></body></html>');
done();
});
});
describe('HTMLDoc Tags', function() {
it('All', function(done) {
var htdoc = new funcs.HTMLDoc('<!DOCTYPE html><html><head></head><body first="abc"></body></html>');
var htparts = htdoc.getParts();
assert(htparts.doctype);
assert(htparts.html);
assert(htparts.head);
assert(htparts.body);
done();
});
it('No Doctype', function(done) {
var htdoc = new funcs.HTMLDoc('<html><head></head><body first="abc"></body></html>');
var htparts = htdoc.getParts();
assert(!htparts.doctype);
assert(htparts.html);
assert(htparts.head);
assert(htparts.body);
done();
});
it('No head', function(done) {
var htdoc = new funcs.HTMLDoc('<html><body first="abc"></body></html>');
var htparts = htdoc.getParts();
assert(!htparts.doctype);
assert(htparts.html);
assert(!htparts.head);
assert(htparts.body);
done();
});
it('No Body', function(done) {
var htdoc = new funcs.HTMLDoc('<html>Test Content</html>');
var htparts = htdoc.getParts();
assert(!htparts.doctype);
assert(htparts.html);
assert(!htparts.head);
assert(!htparts.body);
done();
});
it('No HTML', function(done) {
var htdoc = new funcs.HTMLDoc('Test Content');
var htparts = htdoc.getParts();
assert(!htparts.doctype);
assert(!htparts.html);
assert(!htparts.head);
assert(!htparts.body);
done();
});
it('Only doctype', function(done) {
var htdoc = new funcs.HTMLDoc('<!DOCTYPE html>Test Content');
var htparts = htdoc.getParts();
assert(htparts.doctype);
assert(!htparts.html);
assert(!htparts.head);
assert(!htparts.body);
done();
});
});
describe('Page Editor Template Generation', function() {
it('Full Page', function(done) {
var rslt = funcs.generateEditorTemplate([
'Test',
].join(''));
assert.equal(rslt, [
'<!DOCTYPE HTML><html>',
'<head>',
'</head>',
'<body>',
'<script type="text/javascript" class="removeOnPublish" src="/js/jsHarmonyCMS.js"></script>',
'Test',
'</body>',
'</html>',
].join(''));
done();
});
it('No duplicate script', function(done) {
var rslt = funcs.generateEditorTemplate([
'Test',
'<script type="text/javascript" src="/js/jsHarmonyCMS.js"></script>',
].join(''));
assert.equal(rslt, [
'<!DOCTYPE HTML><html>',
'<head>',
'</head>',
'<body>',
'Test',
'<script type="text/javascript" src="/js/jsHarmonyCMS.js"></script>',
'</body>',
'</html>',
].join(''));
done();
});
it('Doctype', function(done) {
var rslt = funcs.generateEditorTemplate([
'<html>',
'<head>',
'</head>',
'<body>',
'Test',
'</body>',
'</html>',
].join(''));
assert.equal(rslt, [
'<!DOCTYPE HTML><html>',
'<head>',
'</head>',
'<body>',
'<script type="text/javascript" class="removeOnPublish" src="/js/jsHarmonyCMS.js"></script>',
'Test',
'</body>',
'</html>',
].join(''));
done();
});
it('Page Parsing Error', function(done) {
var rslt = funcs.generateEditorTemplate([
'<html>',
'<head>',
'</head>',
'<body>',
'Test<%=abcdef%>Test2<div id="<%=defghji+"abc"%>"></div>',
'</body>',
'</html>',
].join(''));
assert.equal(rslt, [
'<!DOCTYPE HTML><html>',
'<head>',
'</head>',
'<body>',
'<script type="text/javascript" class="removeOnPublish" src="/js/jsHarmonyCMS.js"></script>',
'Test<%=abcdef%>Test2<div id="<%=defghji+"abc"%>"></div>',
'</body>',
'</html>',
].join(''));
done();
});
it('Template Variables', function(done) {
var rslt = funcs.generateEditorTemplate([
'Test %%%URL%%% Test',
].join(''), { template_variables: { 'URL': 'http://test' } });
assert.equal(rslt, [
'<!DOCTYPE HTML><html>',
'<head>',
'</head>',
'<body>',
'<script type="text/javascript" class="removeOnPublish" src="/js/jsHarmonyCMS.js"></script>',
'Test http://test Test',
'</body>',
'</html>',
].join(''));
done();
});
it('cms-page-config', function(done) {
var rslt = funcs.generateEditorTemplate([
'<html>',
'<head>',
'<cms-page-config>{ "id": "test" }</cms-page-config>',
'<title>Title</title>',
'</head>',
'<body>',
'Test',
'</body>',
'</html>',
].join(''));
assert.equal(rslt, [
'<!DOCTYPE HTML><html>',
'<head>',
'<script type="text/cms-page-config">{ "id": "test" }</script>',
'<title>Title</title>',
'</head>',
'<body>',
'<script type="text/javascript" class="removeOnPublish" src="/js/jsHarmonyCMS.js"></script>',
'Test',
'</body>',
'</html>',
].join(''));
done();
});
});
describe('Page Deployment Template Generation', function() {
it('Header Tags Replaced', function(done) {
var rslt = funcs.generateDeploymentTemplate(null, [
'<html>',
'<head>',
'<title>ABC</title>',
'<meta name="keywords" content="The Meta Keywords" />',
'<meta name="description" content="The Meta Description" />',
'<link rel="canonical" href="http://zzzzz" />',
'</head>',
'<body>',
'</body>',
'</html>',
].join(''));
assert.equal(rslt, [
'<html>',
'<head>',
'<% if(page.seo.title){ %><title><%=page.seo.title%></title><% } %>',
'<% if(page.seo.keywords){ %><meta content="<%=page.seo.keywords%>" name="keywords" /><% } %>',
'<% if(page.seo.metadesc){ %><meta content="<%=page.seo.metadesc%>" name="description" /><% } %>',
'<% if(page.seo.canonical_url){ %><link href="<%=page.seo.canonical_url%>" rel="canonical" /><% } %>',
'<% if(page.css){ %><style type="text/css"><%-page.css%></style><% } %>',
'<% if(page.js){ %><script type="text/javascript"><%-page.js%></script><% } %>',
'<%-page.header%>',
'</head>',
'<body>',
'<%-page.footer%>',
'</body>',
'</html>',
].join(''));
done();
});
it('Header Tags Added if HEAD Tag Exists', function(done) {
var rslt = funcs.generateDeploymentTemplate(null, [
'<html>',
'<head>',
'</head>',
'<body>',
'</body>',
'</html>',
].join(''));
assert.equal(rslt, [
'<html>',
'<head>',
'<% if(page.seo.title){ %><title><%=page.seo.title%></title><% } %>',
'<% if(page.seo.keywords){ %><meta name="keywords" content="<%=page.seo.keywords%>" /><% } %>',
'<% if(page.seo.metadesc){ %><meta name="description" content="<%=page.seo.metadesc%>" /><% } %>',
'<% if(page.seo.canonical_url){ %><link rel="canonical" href="<%=page.seo.canonical_url%>" /><% } %>',
'<% if(page.css){ %><style type="text/css"><%-page.css%></style><% } %>',
'<% if(page.js){ %><script type="text/javascript"><%-page.js%></script><% } %>',
'<%-page.header%>',
'</head>',
'<body>',
'<%-page.footer%>',
'</body>',
'</html>',
].join(''));
done();
});
it('Header Tags Added if HTML Tag Exists', function(done) {
var rslt = funcs.generateDeploymentTemplate(null, [
'<html>',
'</html>',
].join(''));
assert.equal(rslt, [
'<html>',
'<% if(page.seo.title){ %><title><%=page.seo.title%></title><% } %>',
'<% if(page.seo.keywords){ %><meta name="keywords" content="<%=page.seo.keywords%>" /><% } %>',
'<% if(page.seo.metadesc){ %><meta name="description" content="<%=page.seo.metadesc%>" /><% } %>',
'<% if(page.seo.canonical_url){ %><link rel="canonical" href="<%=page.seo.canonical_url%>" /><% } %>',
'<% if(page.css){ %><style type="text/css"><%-page.css%></style><% } %>',
'<% if(page.js){ %><script type="text/javascript"><%-page.js%></script><% } %>',
'<%-page.header%>',
'<%-page.footer%>',
'</html>',
].join(''));
done();
});
it('Header Tags Added if BODY Tag Exists', function(done) {
var rslt = funcs.generateDeploymentTemplate(null, [
'<!DOCTYPE html>',
'<html>',
'<body>',
'</body>',
'</html>',
].join(''));
assert.equal(rslt, [
'<!DOCTYPE html>',
'<html>',
'<% if(page.seo.title){ %><title><%=page.seo.title%></title><% } %>',
'<% if(page.seo.keywords){ %><meta name="keywords" content="<%=page.seo.keywords%>" /><% } %>',
'<% if(page.seo.metadesc){ %><meta name="description" content="<%=page.seo.metadesc%>" /><% } %>',
'<% if(page.seo.canonical_url){ %><link rel="canonical" href="<%=page.seo.canonical_url%>" /><% } %>',
'<% if(page.css){ %><style type="text/css"><%-page.css%></style><% } %>',
'<% if(page.js){ %><script type="text/javascript"><%-page.js%></script><% } %>',
'<%-page.header%>',
'<body>',
'<%-page.footer%>',
'</body>',
'</html>',
].join(''));
done();
});
it('Header Tags Added if DOCTYPE Tag Exists', function(done) {
var rslt = funcs.generateDeploymentTemplate(null, [
'<!DOCTYPE html>',
'Test Content',
].join(''));
assert.equal(rslt, [
'<!DOCTYPE html>',
'<% if(page.seo.title){ %><title><%=page.seo.title%></title><% } %>',
'<% if(page.seo.keywords){ %><meta name="keywords" content="<%=page.seo.keywords%>" /><% } %>',
'<% if(page.seo.metadesc){ %><meta name="description" content="<%=page.seo.metadesc%>" /><% } %>',
'<% if(page.seo.canonical_url){ %><link rel="canonical" href="<%=page.seo.canonical_url%>" /><% } %>',
'<% if(page.css){ %><style type="text/css"><%-page.css%></style><% } %>',
'<% if(page.js){ %><script type="text/javascript"><%-page.js%></script><% } %>',
'<%-page.header%>',
'Test Content',
'<%-page.footer%>',
].join(''));
done();
});
it('Header Tags Added if DOCTYPE and BODY Tags Exists', function(done) {
var rslt = funcs.generateDeploymentTemplate(null, [
'<!DOCTYPE html>',
'<body>',
'</body>',
].join(''));
assert.equal(rslt, [
'<!DOCTYPE html>',
'<% if(page.seo.title){ %><title><%=page.seo.title%></title><% } %>',
'<% if(page.seo.keywords){ %><meta name="keywords" content="<%=page.seo.keywords%>" /><% } %>',
'<% if(page.seo.metadesc){ %><meta name="description" content="<%=page.seo.metadesc%>" /><% } %>',
'<% if(page.seo.canonical_url){ %><link rel="canonical" href="<%=page.seo.canonical_url%>" /><% } %>',
'<% if(page.css){ %><style type="text/css"><%-page.css%></style><% } %>',
'<% if(page.js){ %><script type="text/javascript"><%-page.js%></script><% } %>',
'<%-page.header%>',
'<body>',
'<%-page.footer%>',
'</body>',
].join(''));
done();
});
it('Header Tags Added if no tags exist', function(done) {
var rslt = funcs.generateDeploymentTemplate(null, [
'Test Content',
].join(''));
assert.equal(rslt, [
'<% if(page.seo.title){ %><title><%=page.seo.title%></title><% } %>',
'<% if(page.seo.keywords){ %><meta name="keywords" content="<%=page.seo.keywords%>" /><% } %>',
'<% if(page.seo.metadesc){ %><meta name="description" content="<%=page.seo.metadesc%>" /><% } %>',
'<% if(page.seo.canonical_url){ %><link rel="canonical" href="<%=page.seo.canonical_url%>" /><% } %>',
'<% if(page.css){ %><style type="text/css"><%-page.css%></style><% } %>',
'<% if(page.js){ %><script type="text/javascript"><%-page.js%></script><% } %>',
'<%-page.header%>',
'Test Content',
'<%-page.footer%>',
].join(''));
done();
});
it('Component Tag', function(done) {
var rslt = funcs.generateDeploymentTemplate(null, [
'<div cms-component="menus/main.top" cms-menu-tag="main"></div>',
'<div cms-component="analytics" cms-component-remove-container></div>',
'Test Content',
'<div cms-component="sidebar"></div>',
'<div cms-component="tiles" cms-component-properties=\'{"cssClass":"testClass"}\' cms-component-data=\'[{"image":"test"}]\'></div>',
].join(''));
assert.equal(rslt, [
'<% if(page.seo.title){ %><title><%=page.seo.title%></title><% } %>',
'<% if(page.seo.keywords){ %><meta name="keywords" content="<%=page.seo.keywords%>" /><% } %>',
'<% if(page.seo.metadesc){ %><meta name="description" content="<%=page.seo.metadesc%>" /><% } %>',
'<% if(page.seo.canonical_url){ %><link rel="canonical" href="<%=page.seo.canonical_url%>" /><% } %>',
'<% if(page.css){ %><style type="text/css"><%-page.css%></style><% } %>',
'<% if(page.js){ %><script type="text/javascript"><%-page.js%></script><% } %>',
'<%-page.header%>',
'<div><%-renderComponent("menus/main.top", {"menu_tag":"main"})%></div>',
'<%-renderComponent("analytics")%>',
'Test Content',
'<div><%-renderComponent("sidebar")%></div>',
'<div><%-renderComponent("tiles", {"properties":{"cssClass":"testClass"},"data":[{"image":"test"}]})%></div>',
'<%-page.footer%>',
].join(''));
done();
});
it('Page Component', function(done) {
var rslt = funcs.generateDeploymentTemplate(null, [
'<div cms-component="analytics"></div>',
'Test Content',
].join(''));
assert.equal(rslt, [
'<% if(page.seo.title){ %><title><%=page.seo.title%></title><% } %>',
'<% if(page.seo.keywords){ %><meta name="keywords" content="<%=page.seo.keywords%>" /><% } %>',
'<% if(page.seo.metadesc){ %><meta name="description" content="<%=page.seo.metadesc%>" /><% } %>',
'<% if(page.seo.canonical_url){ %><link rel="canonical" href="<%=page.seo.canonical_url%>" /><% } %>',
'<% if(page.css){ %><style type="text/css"><%-page.css%></style><% } %>',
'<% if(page.js){ %><script type="text/javascript"><%-page.js%></script><% } %>',
'<%-page.header%>',
'<div><%-renderComponent("analytics")%></div>',
'Test Content',
'<%-page.footer%>',
].join(''));
done();
});
it('Containerless Page Component', function(done) {
var rslt = funcs.generateDeploymentTemplate(null, [
'<div cms-component="analytics" cms-component-remove-container></div>',
'Test Content',
].join(''));
assert.equal(rslt, [
'<% if(page.seo.title){ %><title><%=page.seo.title%></title><% } %>',
'<% if(page.seo.keywords){ %><meta name="keywords" content="<%=page.seo.keywords%>" /><% } %>',
'<% if(page.seo.metadesc){ %><meta name="description" content="<%=page.seo.metadesc%>" /><% } %>',
'<% if(page.seo.canonical_url){ %><link rel="canonical" href="<%=page.seo.canonical_url%>" /><% } %>',
'<% if(page.css){ %><style type="text/css"><%-page.css%></style><% } %>',
'<% if(page.js){ %><script type="text/javascript"><%-page.js%></script><% } %>',
'<%-page.header%>',
'<%-renderComponent("analytics")%>',
'Test Content',
'<%-page.footer%>',
].join(''));
done();
});
it('Containerless Page Component with onRender', function(done) {
var rslt = funcs.generateDeploymentTemplate(null, [
'<div cms-component="analytics" cms-component-remove-container cms-onRender="showIf(page.properties.showAnalytics!=\'N\');"></div>',
'Test Content',
].join(''));
assert.equal(rslt, [
'<% if(page.seo.title){ %><title><%=page.seo.title%></title><% } %>',
'<% if(page.seo.keywords){ %><meta name="keywords" content="<%=page.seo.keywords%>" /><% } %>',
'<% if(page.seo.metadesc){ %><meta name="description" content="<%=page.seo.metadesc%>" /><% } %>',
'<% if(page.seo.canonical_url){ %><link rel="canonical" href="<%=page.seo.canonical_url%>" /><% } %>',
'<% if(page.css){ %><style type="text/css"><%-page.css%></style><% } %>',
'<% if(page.js){ %><script type="text/javascript"><%-page.js%></script><% } %>',
'<%-page.header%>',
'<script id="jshcms-component-containerless-1-start" cms-onRender-range="showIf(page.properties.showAnalytics!='N');" cms-onRender-rangeId="jshcms-component-containerless-1"></script>',
'<%-renderComponent("analytics")%>',
'<script id="jshcms-component-containerless-1-end"></script>',
'Test Content',
'<%-page.footer%>',
].join(''));
var page = {
seo: {
title: 'Test Title',
keywords: 'Test Keywords',
metadesc: 'Test Metadesc',
canonical_url: 'Test Canonical URL',
},
css: 'body { border: 1px solid red; }',
js: 'alert("test");',
header: 'Page Header',
footer: 'Page Footer',
properties: {
showAnalytics: 'Y',
}
};
function renderPage(_page){
var content = ejs.render(rslt, {
page: _page,
renderComponent: function(id, renderOptions){ return 'analytics<br/><div>Test</div>'; },
});
content = funcs.applyRenderTags(content, { page: _page });
return content;
}
assert.equal(renderPage(page), [
'<title>Test Title</title>',
'<meta name="keywords" content="Test Keywords" />',
'<meta name="description" content="Test Metadesc" />',
'<link rel="canonical" href="Test Canonical URL" />',
'<style type="text/css">body { border: 1px solid red; }</style>',
'<script type="text/javascript">alert("test");</script>',
'Page Header',
'analytics<br/><div>Test</div>',
'Test Content',
'Page Footer',
].join(''));
page.properties.showAnalytics = 'N';
assert.equal(renderPage(page), [
'<title>Test Title</title>',
'<meta name="keywords" content="Test Keywords" />',
'<meta name="description" content="Test Metadesc" />',
'<link rel="canonical" href="Test Canonical URL" />',
'<style type="text/css">body { border: 1px solid red; }</style>',
'<script type="text/javascript">alert("test");</script>',
'Page Header',
'Test Content',
'Page Footer',
].join(''));
done();
});
it('Inline Component Template', function(done) {
var template = {};
var rslt = funcs.generateDeploymentTemplate(template, [
'<script type="text/cms-component-template">',
'<cms-component-config>{ "id": "menus/top", "target": "page", }</cms-component-config>',
'<ul class="links">',
'<li jsh-foreach-item="menu.topItems"><a href="<%~item.href%>" onclick="<%~item.onclick%>" target="<%~item.target%>"><%-item.html%></a></li>',
'</ul>',
'</script>',
'<div cms-component="menus/main.top" cms-menu-tag="main"></div>',
'Test Content',
'<div cms-component="sidebar"></div>',
'<div cms-component="tiles" cms-component-properties=\'{"cssClass":"testClass"}\' cms-component-data=\'[{"image":"test"}]\'></div>',
].join(''));
assert.equal(rslt, [
'<% if(page.seo.title){ %><title><%=page.seo.title%></title><% } %>',
'<% if(page.seo.keywords){ %><meta name="keywords" content="<%=page.seo.keywords%>" /><% } %>',
'<% if(page.seo.metadesc){ %><meta name="description" content="<%=page.seo.metadesc%>" /><% } %>',
'<% if(page.seo.canonical_url){ %><link rel="canonical" href="<%=page.seo.canonical_url%>" /><% } %>',
'<% if(page.css){ %><style type="text/css"><%-page.css%></style><% } %>',
'<% if(page.js){ %><script type="text/javascript"><%-page.js%></script><% } %>',
'<%-page.header%>',
'<div><%-renderComponent("menus/main.top", {"menu_tag":"main"})%></div>',
'Test Content',
'<div><%-renderComponent("sidebar")%></div>',
'<div><%-renderComponent("tiles", {"properties":{"cssClass":"testClass"},"data":[{"image":"test"}]})%></div>',
'<%-page.footer%>',
].join(''));
assert(template.components['menus/top'].title == 'menus/top');
done();
});
it('Inline Component Template Inside Content Area', function(done) {
var template = {};
var rslt = funcs.generateDeploymentTemplate(template, [
'<div cms-content-editor="page.content.body">',
'<script type="text/cms-component-template">',
'<cms-component-config>{ "id": "menus/top", "target": "page", }</cms-component-config>',
'<ul class="links">',
'<li jsh-foreach-item="menu.topItems"><a href="<%~item.href%>" onclick="<%~item.onclick%>" target="<%~item.target%>"><%-item.html%></a></li>',
'</ul>',
'</script>',
'<div cms-component="menus/main.top" cms-menu-tag="main"></div>',
'Test Content',
'<div cms-component="sidebar"></div>',
'<div cms-component="tiles" cms-component-properties=\'{"cssClass":"testClass"}\' cms-component-data=\'[{"image":"test"}]\'></div>',
'</div>',
].join(''));
assert.equal(rslt, [
'<% if(page.seo.title){ %><title><%=page.seo.title%></title><% } %>',
'<% if(page.seo.keywords){ %><meta name="keywords" content="<%=page.seo.keywords%>" /><% } %>',
'<% if(page.seo.metadesc){ %><meta name="description" content="<%=page.seo.metadesc%>" /><% } %>',
'<% if(page.seo.canonical_url){ %><link rel="canonical" href="<%=page.seo.canonical_url%>" /><% } %>',
'<% if(page.css){ %><style type="text/css"><%-page.css%></style><% } %>',
'<% if(page.js){ %><script type="text/javascript"><%-page.js%></script><% } %>',
'<%-page.header%>',
'<div><%-page.content["body"]%></div>',
'<%-page.footer%>',
].join(''));
assert(template.components['menus/top'].title == 'menus/top');
done();
});
it('Template Variables', function(done) {
var rslt = funcs.generateDeploymentTemplate(null, [
'<html>',
'<head>',
'</head>',
'<body>',
'Test %%%URL%%% Test',
'</body>',
'</html>',
].join(''), { template_variables: { 'URL': 'http://test' } });
assert.equal(rslt, [
'<html>',
'<head>',
'<% if(page.seo.title){ %><title><%=page.seo.title%></title><% } %>',
'<% if(page.seo.keywords){ %><meta name="keywords" content="<%=page.seo.keywords%>" /><% } %>',
'<% if(page.seo.metadesc){ %><meta name="description" content="<%=page.seo.metadesc%>" /><% } %>',
'<% if(page.seo.canonical_url){ %><link rel="canonical" href="<%=page.seo.canonical_url%>" /><% } %>',
'<% if(page.css){ %><style type="text/css"><%-page.css%></style><% } %>',
'<% if(page.js){ %><script type="text/javascript"><%-page.js%></script><% } %>',
'<%-page.header%>',
'</head>',
'<body>',
'Test http://test Test',
'<%-page.footer%>',
'</body>',
'</html>',
].join(''));
done();
});
});
describe('EJS Extract / Restore', function() {
it('Simple EJS', function(done) {
var htdoc = new funcs.HTMLDoc([
'<div class="<%=test%>">',
'Test Content',
'</div>',
].join(''), { extractEJS: true });
htdoc.applyNodes([
{ //Apply properties
pred: function(node){ return htdoc.isTag(node, 'div'); },
exec: function(node){
htdoc.appendAttr(node, 'class', 'moreClass', ' ');
}
},
]);
htdoc.restoreEJS();
assert.equal(htdoc.content, [
'<div class="<%=test%> moreClass">',
'Test Content',
'</div>',
].join(''));
done();
});
it('More EJS', function(done) {
var htdoc = new funcs.HTMLDoc([
'<div class="<%=test%>">',
'Test Content <%=value%>',
'</div>',
].join(''), { extractEJS: true });
htdoc.applyNodes([
{ //Apply properties
pred: function(node){ return htdoc.isTag(node, 'div'); },
exec: function(node){
htdoc.appendAttr(node, 'class', 'moreClass', ' ');
htdoc.appendAttr(node, 'style', 'basicStyle', ';');
htdoc.appendAttr(node, 'style', 'moreStyle', ';');
}
},
]);
htdoc.restoreEJS();
assert.equal(htdoc.content, [
'<div style="basicStyle;moreStyle" class="<%=test%> moreClass">',
'Test Content <%=value%>',
'</div>',
].join(''));
done();
});
it('Extract EJS parseOnly', function(done) {
var htdoc = new funcs.HTMLDoc([
'<div class="<%=test%>">',
'Test Content <%=value%>',
'</div>',
].join(''), { extractEJS: 'parseOnly' });
var nodeContent = '';
htdoc.applyNodes([
{ //Apply properties
pred: function(node){ return htdoc.isTag(node, 'div'); },
exec: function(node){
nodeContent = htdoc.getNodeContent(node);
}
},
]);
assert.equal(nodeContent, [
'Test Content <%=value%>',
].join(''));
done();
});
it('EJS with Removed Attribute', function(done) {
var htdoc = new funcs.HTMLDoc([
'<div class="<%=test%>" style="<%=initialStyle%>">',
'Test Content <%=value%>',
'</div>',
].join(''), { extractEJS: true });
htdoc.applyNodes([
{ //Apply properties
pred: function(node){ return htdoc.isTag(node, 'div'); },
exec: function(node){
htdoc.removeAttr(node, 'style');
htdoc.appendAttr(node, 'class', 'moreClass', ' ');
htdoc.appendAttr(node, 'style', 'basicStyle', ';');
htdoc.appendAttr(node, 'style', 'moreStyle', ';');
}
},
]);
htdoc.restoreEJS();
htdoc.trimRemoved();
assert.equal(htdoc.content, [
'<div style="basicStyle;moreStyle" class="<%=test%> moreClass">',
'Test Content <%=value%>',
'</div>',
].join(''));
done();
});
it('Header Tag with EJS', function(done) {
var htdoc = new funcs.HTMLDoc([
'<%=before%><header><%=header_var%></header><%=after%>',
'<%=before2%><div class="<%=test%>" style="<%=initialStyle%>">',
'Test Content <%=value%>',
'</div><%=after2%>',
].join(''), { extractEJS: true });
htdoc.applyNodes([
{ //Remove header
pred: function(node){ return htdoc.isTag(node, 'header'); },
exec: function(node){
htdoc.removeNode(node);
}
},
{ //Apply properties
pred: function(node){ return htdoc.isTag(node, 'div'); },
exec: function(node){
htdoc.removeAttr(node, 'style');
htdoc.appendAttr(node, 'class', 'moreClass', ' ');
htdoc.appendAttr(node, 'style', 'basicStyle', ';');
htdoc.appendAttr(node, 'style', 'moreStyle', ';');
htdoc.wrapNode(node, '<%=wrapPre%>', '<%=wrapPost%>');
}
},
]);
htdoc.restoreEJS();
htdoc.trimRemoved();
assert.equal(htdoc.content, [
'<%=before%><%=after%><%=before2%><%=wrapPre%><div style="basicStyle;moreStyle" class="<%=test%> moreClass">',
'Test Content <%=value%>',
'</div><%=wrapPost%><%=after2%>',
].join(''));
done();
});
});
describe('Component Template Generation', function() {
it('Error on duplicate tag', function(done) {
assert.throws(function(){
funcs.generateComponentTemplate(null, [
'<div class="inner flex flex-3" jsh-group-items-into="3" jsh-group-items-into="3">',
'</div>',
].join(''));
}, /Duplicate attribute "jsh-group-items-into"/);
done();
});
it('Basic Component Generated', function(done) {
var componentConfig = { data: { fields: [ { name: 'title' } ] } };
var rslt = funcs.generateComponentTemplate(componentConfig, [
'<script type="text/cms-component-config">',
'{ "title": "Local Tiles <%=sampleEjs%>" }',
'</script>',
'<section class="tiles wrapper <%=component.cssClass%>" style="<%=component.cssStyle%>" cms-component-editor-add-class="preview">',
' <div class="inner flex flex-3" jsh-group-items-into="3" cms-component-editor-remove-class="flex-3">',
' <div jsh-foreach-item class="flex-item box <%=item.cssClass%>">',
' <div class="image fit">',
' <img src="<%=item.image%>" alt="<%=item.title%>" />',
' </div>',
' <div class="content">',
' <h3 cms-content-editor="item.title" cms-content-editor-type="simple">Head</h3>',
' <p cms-content-editor="item.body" cms-editor-on-p></p>',
' </div>',
' </div>',
' </div>',
'</section>',
].join(''));
assert.equal(rslt, [
componentPrefix,
'<section class="tiles wrapper <%=component.cssClass%> <%=(isInComponentEditor?"preview":"")%>" style="<%=component.cssStyle%>">',
' <% (function(){ let jsh_groups = (function(groupFunc, items){let rslt = {};for(let i=0;i<items.length;i++){let key = groupFunc(items[i], i);if(!(key in rslt)) rslt[key] = [];rslt[key].push(items[i]);}return rslt;})(function(item, jsh_group_item_index){return (Math.floor(jsh_group_item_index/3)+1);}, items); let jsh_subgroup_first = true; for(let jsh_group_index in jsh_groups){ let items=jsh_groups[jsh_group_index]; if(jsh_subgroup_first){ jsh_subgroup_first = false; }else{ %><% } (function(){ %><div class="inner flex <%=(!isInComponentEditor?"flex-3":"")%>">',
' <% for(let jsh_item_index=(1);jsh_item_index<=(((items)||[]).length);jsh_item_index+=(1+0)){ let item = ((items)||[])[jsh_item_index-1]; if(jsh_item_index>(1)){ %><% } if((item)&&(item).jsh_validation_errors){ %><%-renderPlaceholder({ errors: (item).jsh_validation_errors })%><% } else { %><div class="flex-item box <%=item.cssClass%>">',
' <div class="image fit">',
' <img src="<%=item.image%>" alt="<%=item.title%>" />',
' </div>',
' <div class="content">',
' <h3 <% if(renderType=="gridItemPreview"){ %>data-component-title-editor="title"<% } %>><%-item.title%></h3>',
' <p <% if(renderType=="gridItemPreview"){ %>data-component-full-editor="body"<% } %>><%-item.body%></p>',
' </div>',
' </div><% } } %>',
' </div><% })(); } })(); %>',
'</section>',
].join(''));
assert(componentConfig.data.fields[0].default == 'Head');
done();
});
it('Component Config Tag', function(done) {
var templateContent = [
'<cms-component-config>',
'{ "title": "Local Tiles" }',
'</cms-component-config>',
'<div jsh-foreach-item class="flex-item box <%=item.cssClass%>">',
'<img src="<%=item.image%>" alt="<%=item.title%>" />',
'</div>',
].join('');
var templateParts = funcs.parseConfig(templateContent, 'cms-component-config', 'cms-component-config', { extractFromContent: true });
assert.equal(templateParts.content, [
'<div jsh-foreach-item class="flex-item box <%=item.cssClass%>">',
'<img src="<%=item.image%>" alt="<%=item.title%>" />',
'</div>',
].join(''));
assert.deepEqual(templateParts.config, { title: 'Local Tiles' });
var rslt = funcs.generateComponentTemplate(templateParts.config, templateContent);
assert.equal(rslt, [
componentPrefix,
'<% for(let jsh_item_index=(1);jsh_item_index<=(((items)||[]).length);jsh_item_index+=(1+0)){ let item = ((items)||[])[jsh_item_index-1]; if(jsh_item_index>(1)){ %><% } if((item)&&(item).jsh_validation_errors){ %><%-renderPlaceholder({ errors: (item).jsh_validation_errors })%><% } else { %><div class="flex-item box <%=item.cssClass%>">',
'<img src="<%=item.image%>" alt="<%=item.title%>" />',
'</div><% } } %>',
].join(''));
done();
});
it('containerSlurp - outside of node', function(done) {
assert.throws(function(){
funcs.generateComponentTemplate(null, [
'<ul jsh-template="menu"></ul>',
'<%~test%>',
].join(''));
}, /must be inside an HTML element/);
done();
});
it('containerSlurp - extra attribute', function(done) {
assert.throws(function(){
funcs.generateComponentTemplate(null, [
'<ul jsh-template="menu" style="<%~test%> "></ul>',
].join(''));
}, /must be the only text within/);
done();
});
it('containerSlurp - extra content', function(done) {
assert.throws(function(){
funcs.generateComponentTemplate(null, [
'<ul jsh-template="menu"><%~test%> </ul>',
].join(''));
}, /must be the only text within/);
done();
});
it('containerSlurp - start tag', function(done) {
assert.throws(function(){
funcs.generateComponentTemplate(null, [
'<ul jsh-template="menu" <%~test%>></ul>',
].join(''));
}, /can only be used for attribute values or HTML element content/);
done();
});
it('containerSlurp - end tag', function(done) {
assert.throws(function(){
funcs.generateComponentTemplate(null, [
'<ul jsh-template="menu"></ul <%~test%>>',
].join(''));
}, /can only be used for attribute values or HTML element content/);
done();
});
it('containerSlurp - parent deleted', function(done) {
var rslt = funcs.generateComponentTemplate(null, [
'<script type="text/cms-component-config"><%~test%></script>',
].join(''));
assert.equal(rslt, componentPrefix);
done();
});
it('containerSlurp - attribute', function(done) {
var rslt = funcs.generateComponentTemplate(null, [
'<ul style="<%~test%>"></ul>',
].join(''));
assert.equal(rslt, [
componentPrefix,
'<ul<% if(!isNullUndefinedEmpty(test)){ %> style="<%=test%>"<% } %>></ul>',
].join(''));
done();
});
it('containerSlurp - content', function(done) {
var rslt = funcs.generateComponentTemplate(null, [
'<ul><%~test%></ul>',
].join(''));
assert.equal(rslt, [
componentPrefix,
'<% if(!isNullUndefinedEmpty(test)){ %><ul><%=test%></ul><% } %>',
].join(''));
done();
});
it('containerSlurp - <%~=', function(done) {
var rslt = funcs.generateComponentTemplate(null, [
'<ul><%~=test%></ul>',
].join(''));
assert.equal(rslt, [
componentPrefix,
'<% if(!isNullUndefinedEmpty(test)){ %><ul><%= test%></ul><% } %>',
].join(''));
done();
});
it('containerSlurp - <%~-', function(done) {
var rslt = funcs.generateComponentTemplate(null, [
'<ul><%~-test%></ul>',
].join(''));
assert.equal(rslt, [
componentPrefix,
'<% if(!isNullUndefinedEmpty(test)){ %><ul><%- test%></ul><% } %>',
].join(''));
done();
});
it('containerSlurp - <%~_', function(done) {
var rslt = funcs.generateComponentTemplate(null, [
'<ul><%~_test%></ul>',
].join(''));
assert.equal(rslt, [
componentPrefix,
'<% if(!isNullUndefinedEmpty(test)){ %><ul><%_ test%></ul><% } %>',
].join(''));
done();
});
it('containerSlurp - -%>', function(done) {
var rslt = funcs.generateComponentTemplate(null, [
'<ul><%~_test-%></ul>',
].join(''));
assert.equal(rslt, [
componentPrefix,
'<% if(!isNullUndefinedEmpty(test)){ %><ul><%_ test-%></ul><% } %>',
].join(''));
done();
});
it('containerSlurp - _%>', function(done) {
var rslt = funcs.generateComponentTemplate(null, [
'<ul><%~test_%></ul>',
].join(''));
assert.equal(rslt, [
componentPrefix,
'<% if(!isNullUndefinedEmpty(test)){ %><ul><%=test_%></ul><% } %>',
].join(''));
done();
});
it('Foreach / Templates', function(done) {
var rslt = funcs.generateComponentTemplate(null, [
'<ul jsh-template="menu">',
' <li jsh-foreach-item class="<%~item.class%>" style="<%~item.style%>">',
' <a href="<%~item.href%>" onclick="<%~item.onclick%>" target="<%~item.target%>"><%~-item.html%></a>',
' <%-renderTemplate("menu", item.children)%>',
' </li>',
'</ul>',
'<%-renderTemplate("menu", menu.tree)%>',
].join(''));
assert.equal(rslt, [
componentPrefix,
'<% (locals.jsh_render_templates=locals.jsh_render_templates||{})["menu"] = function(items){ %>',
'<ul>',
' <% for(let jsh_item_index=(1);jsh_item_index<=(((items)||[]).length);jsh_item_index+=(1+0)){ let item = ((items)||[])[jsh_item_index-1]; if(jsh_item_index>(1)){ %><% } if((item)&&(item).jsh_validation_errors){ %><%-renderPlaceholder({ errors: (item).jsh_validation_errors })%><% } else { %>',
'<li<% if(!isNullUndefinedEmpty(item.class)){ %> class="<%=item.class%>"<% } %><% if(!isNullUndefinedEmpty(item.style)){ %> style="<%=item.style%>"<% } %>>',
' <% if(!isNullUndefinedEmpty(item.html)){ %><a<% if(!isNullUndefinedEmpty(item.href)){ %> href="<%=item.href%>"<% } %><% if(!isNullUndefinedEmpty(item.onclick)){ %> onclick="<%=item.onclick%>"<% } %><% if(!isNullUndefinedEmpty(item.target)){ %> target="<%=item.target%>"<% } %>><%- item.html%></a><% } %>',
' <%-renderTemplate("menu", item.children)%>',
' </li><% } } %>',
'</ul>',
'<% } %><%-renderTemplate("menu", menu.tree)%>',
].join(''));
done();
});
it('Template Variables', function(done) {
var componentConfig = { data: { fields: [ { name: 'title' } ] } };
var rslt = funcs.generateComponentTemplate(componentConfig, [
'<section class="tiles wrapper <%=component.cssClass%>" style="<%=component.cssStyle%>" cms-component-editor-add-class="preview">',
'Test %%%url%%% Test',
'</section>',
].join(''), { template_variables: { url: 'http://test' } });
assert.equal(rslt, [
componentPrefix,
'<section class="tiles wrapper <%=component.cssClass%> <%=(isInComponentEditor?"preview":"")%>" style="<%=component.cssStyle%>">',
'Test http://test Test',
'</section>',
].join(''));
done();
});
});
describe('Menu Generation', function() {
it('Basic Menu Rendering', function(done) {
var menu = {
'menu_items': [
{'menu_item_id':2,'menu_item_parent_id':null,'menu_item_path':'/2/','menu_item_type':'TEXT','menu_item_text':'Home','menu_item_link_type':'PAGE','menu_item_link_dest':'1'},
{'menu_item_id':3,'menu_item_parent_id':null,'menu_item_path':'/3/','menu_item_type':'TEXT','menu_item_text':'About','menu_item_link_type':'PAGE','menu_item_link_dest':'2'},
{'menu_item_id':7,'menu_item_parent_id':3,'menu_item_path':'/3/7/','menu_item_type':'TEXT','menu_item_text':'Careers','menu_item_link_type':'PAGE','menu_item_link_dest':'14'},
{'menu_item_id':8,'menu_item_parent_id':7,'menu_item_path':'/3/7/8/','menu_item_type':'TEXT','menu_item_text':'Designer','menu_item_link_type':'PAGE','menu_item_link_dest':'14'},
{'menu_item_id':9,'menu_item_parent_id':7,'menu_item_path':'/3/7/9/','menu_item_type':'TEXT','menu_item_text':'Developer','menu_item_link_type':'PAGE','menu_item_link_dest':'14'},
{'menu_item_id':4,'menu_item_parent_id':null,'menu_item_path':'/4/','menu_item_type':'TEXT','menu_item_text':'Services','menu_item_link_type':'PAGE','menu_item_link_dest':'3'},
{'menu_item_id':6,'menu_item_parent_id':null,'menu_item_path':'/6/','menu_item_type':'TEXT','menu_item_text':'Testimonials','menu_item_link_type':'PAGE','menu_item_link_dest':'11'},
{'menu_item_id':5,'menu_item_parent_id':null,'menu_item_path':'/5/','menu_item_type':'TEXT','menu_item_text':'Contact','menu_item_link_type':'PAGE','menu_item_link_dest':'8'}
]
};
funcs.createMenuTree(menu);
var template = funcs.generateComponentTemplate(null, [
'<%=iif(menu.tree.length>3,"Y","N")%>',
'<%=iif(menu.tree.length<=3,"Y","N")%>',
'<ul jsh-template="menu">',
' <li jsh-foreach-item class="<%~item.class%>" style="<%~item.style%>">',
' <a href="<%~item.href%>" onclick="<%~item.onclick%>" target="<%~item.target%>"><%-item.html%></a>',
' <%-renderTemplate("menu", item.children)%>',
' </li>',
'</ul>',
'<%-renderTemplate("menu", menu.tree)%>',
].join(''));
var rslt = funcs.renderComponent(template, null, null, { menu: menu });
assert.equal(rslt, [
'YN<ul>',
' <li>',
' <a href="#">Home</a> ',
' </li>',
'<li>',
' <a href="#">About</a>',
' <ul>',
' <li>',
' <a href="#">Careers</a>',
' <ul>',
' <li>',
' <a href="#">Designer</a> ',
' </li>',
'<li>',
' <a href="#">Developer</a> ',
' </li>',
'</ul>',
' </li>',
'</ul>',
' </li>',
'<li>',
' <a href="#">Services</a> ',
' </li>',
'<li>',
' <a href="#">Testimonials</a> ',
' </li>',
'<li>',
' <a href="#">Contact</a> ',
' </li>',
'</ul>',
].join(''));
assert.equal(template, [
componentPrefix,
'<%=iif(menu.tree.length>3,"Y","N")%>',
'<%=iif(menu.tree.length<=3,"Y","N")%>',
'<% (locals.jsh_render_templates=locals.jsh_render_templates||{})["menu"] = function(items){ %>',
'<ul>',
' <% for(let jsh_item_index=(1);jsh_item_index<=(((items)||[]).length);jsh_item_index+=(1+0)){ let item = ((items)||[])[jsh_item_index-1]; if(jsh_item_index>(1)){ %><% } if((item)&&(item).jsh_validation_errors){ %><%-renderPlaceholder({ errors: (item).jsh_validation_errors })%><% } else { %>',
'<li<% if(!isNullUndefinedEmpty(item.class)){ %> class="<%=item.class%>"<% } %><% if(!isNullUndefinedEmpty(item.style)){ %> style="<%=item.style%>"<% } %>>',
' <a<% if(!isNullUndefinedEmpty(item.href)){ %> href="<%=item.href%>"<% } %><% if(!isNullUndefinedEmpty(item.onclick)){ %> onclick="<%=item.onclick%>"<% } %><% if(!isNullUndefinedEmpty(item.target)){ %> target="<%=item.target%>"<% } %>><%-item.html%></a>',
' <%-renderTemplate("menu", item.children)%>',
' </li><% } } %>',
'</ul>',
'<% } %><%-renderTemplate("menu", menu.tree)%>',
].join(''));
done();
});
});
describe('Template tags', function() {
var menu = {
'menu_items': [
{'menu_item_id':2,'menu_item_parent_id':null,'menu_item_path':'/2/','menu_item_type':'TEXT','menu_item_text':'Home','menu_item_link_type':'PAGE','menu_item_link_dest':'1'},
{'menu_item_id':3,'menu_item_parent_id':null,'menu_item_path':'/3/','menu_item_type':'TEXT','menu_item_text':'About','menu_item_link_type':'PAGE','menu_item_link_dest':'2','menu_item_class':'highlight'},
{'menu_item_id':7,'menu_item_parent_id':3,'menu_item_path':'/3/7/','menu_item_type':'TEXT','menu_item_text':'Careers','menu_item_link_type':'PAGE','menu_item_link_dest':'14'},
{'menu_item_id':8,'menu_item_parent_id':7,'menu_item_path':'/3/7/8/','menu_item_type':'TEXT','menu_item_text':'Designer','menu_item_link_type':'MEDIA','menu_item_link_dest':'14'},
{'menu_item_id':9,'menu_item_parent_id':7,'menu_item_path':'/3/7/9/','menu_item_type':'TEXT','menu_item_text':'Developer','menu_item_link_type':'MEDIA','menu_item_link_dest':'14'},
{'menu_item_id':4,'menu_item_parent_id':null,'menu_item_path':'/4/','menu_item_type':'TEXT','menu_item_text':'Services','menu_item_link_type':'PAGE','menu_item_link_dest':'3'},
{'menu_item_id':6,'menu_item_parent_id':null,'menu_item_path':'/6/','menu_item_type':'TEXT','menu_item_text':'Testimonials','menu_item_link_type':'PAGE','menu_item_link_dest':'11'},
{'menu_item_id':5,'menu_item_parent_id':null,'menu_item_path':'/5/','menu_item_type':'TEXT','menu_item_text':'Contact','menu_item_link_type':'PAGE','menu_item_link_dest':'8'}
]
};
var errorMenu = JSON.parse(JSON.stringify(menu));
errorMenu.menu_items[1].jsh_validation_errors = 'Test error';
errorMenu.menu_items[6].jsh_validation_errors = 'Test error2';
funcs.createMenuTree(menu);
funcs.createMenuTree(errorMenu);
it('jsh-group-items', function(done) {
var template = funcs.generateComponentTemplate(null, [
'<div jsh-group-items="menu.tree" jsh-group-items-into="3" class="container">',
'<div jsh-foreach-item class="<%~item.class%>" style="<%~item.style%>"><%-item.html%></div>',
'</div>',
].join(''));
var rslt = funcs.renderComponent(template, null, null, { menu: menu });
assert.equal(rslt, [
'<div class="container">',
'<div>Home</div>',
'<div class="highlight">About</div>',
'<div>Services</div>