diff --git a/cartridges/int_styla/cartridge/controllers/StylaMagazine.js b/cartridges/int_styla/cartridge/controllers/StylaMagazine.js index 24c15d9..25be244 100755 --- a/cartridges/int_styla/cartridge/controllers/StylaMagazine.js +++ b/cartridges/int_styla/cartridge/controllers/StylaMagazine.js @@ -4,18 +4,18 @@ * Controller exposes methods for injecting Styla JavaScript and SEO content. * * @module controllers/StylaMagazine - * - * + * + * * Do not reference the storefront controller cartridge here (e.g to re-use 'app' or 'guard'): * This montroller module is also called via remote include even when the storefront uses pipelines. * IOW: the storefront controller cartridge may or may not exist when this controller executes. - * + * */ var Logger = require('dw/system/Logger').getLogger('styla', 'StylaMagazine'); var ISML = require('dw/template/ISML'); -var StylaMain = require('/int_styla/cartridge/scripts/StylaMain'); +var StylaMain = require('/int_styla/cartridge/scripts/stylaMain'); const CONFIG_CO_TYPE = 'StylaMagazineConfiguration'; // custom object type for storing magazine configurations @@ -31,7 +31,7 @@ function renderContent(template) { } else { ISML.renderTemplate('styla/empty'); - } + } } @@ -55,41 +55,41 @@ function bodyContent() { * Render the Styla cartridge version. */ function cartridgeVersion() { - + var versionInfo = { version: require('~/package.json').cartridgeVersion }, str; str = JSON.stringify(versionInfo, null, '\t'); - + response.setContentType('application/json'); response.setExpires(5 * 60 * 1000); // 5 minutes - response.writer.print(str); + response.writer.print(str); } /** - * If the current URL is part of a magazine, then jump to the corresponding + * If the current URL is part of a magazine, then jump to the corresponding * controller method so that the original URL is preserved in the browser. - * + * * This is called from RedirectURL.start() if no matching redirect rule was found. - * + * * E.g. assume we have an alias 'magazine' assigned to a pipeline which renders a Styla magazine. - * When interacting with the magazine the Styla JavaScript will modify the URL in the + * When interacting with the magazine the Styla JavaScript will modify the URL in the * customer's browser to e.g. 'magazine/stories/5'. - * Because RedirectUrl.start() doesn't find a matching rule for 'magazine/stories/5' - * it calls this function. - * + * Because RedirectUrl.start() doesn't find a matching rule for 'magazine/stories/5' + * it calls this function. + * * @param path Original URL before redirect. - * @returns True, if a matching magazine configuration was found and the configured + * @returns True, if a matching magazine configuration was found and the configured * controller method was called successfully. */ function alias(path) { var result = false, magazineConfig = StylaMain.GetConfigForAlias(path), parts; - + if (magazineConfig) { // read controller method method from configuration parts = magazineConfig.pipeline.split('-'); @@ -127,7 +127,7 @@ function alias(path) { Logger.debug('no matching config found for path: ' + path); } - + return result; } diff --git a/cartridges/int_styla/cartridge/pipelines/StylaMagazine.xml b/cartridges/int_styla/cartridge/pipelines/StylaMagazine.xml deleted file mode 100755 index 8f315fd..0000000 --- a/cartridges/int_styla/cartridge/pipelines/StylaMagazine.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - If the current URL is part of a magazine, then read the pipeline name from the corresponding magazine configuration. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/cartridges/int_styla/cartridge/scripts/StylaMain.js b/cartridges/int_styla/cartridge/scripts/StylaMain.js index 271efa6..19ad24d 100755 --- a/cartridges/int_styla/cartridge/scripts/StylaMain.js +++ b/cartridges/int_styla/cartridge/scripts/StylaMain.js @@ -7,8 +7,8 @@ */ var CustomObjectMgr = require('dw/object/CustomObjectMgr'); -var Logger = require('dw/system/Logger').getLogger('styla', 'StylaMain'); -var StylaServiceInit = require('./init/StylaServiceInit'); +var Logger = require('dw/system/Logger').getLogger('styla', 'stylaMain'); +var StylaServiceInit = require('./init/stylaServiceInit'); var Site = require('dw/system/Site'); var CONFIG_CO_TYPE = 'StylaMagazineConfiguration'; // custom object type for storing magazine configurations diff --git a/cartridges/int_styla/cartridge/scripts/pipelets/GetAliasPipeline.js b/cartridges/int_styla/cartridge/scripts/pipelets/GetAliasPipeline.js deleted file mode 100755 index 138a2ca..0000000 --- a/cartridges/int_styla/cartridge/scripts/pipelets/GetAliasPipeline.js +++ /dev/null @@ -1,51 +0,0 @@ -/** -* If the current URL is part of a magazine, then read the pipeline name from the corresponding magazine configuration. -* -* @input RedirectOrigin : String Original URL before redirect. -* @output PipelineName : String Pipeline to redirect to. Null if no matching magazine configuration was found for the current request path. -* -*/ - -function execute(args : PipelineDictionary) : Number -{ - var Logger = require('dw/system/Logger').getLogger('styla', 'GetAliasPipeline'); - var StylaMain = require('/int_styla/cartridge/scripts/StylaMain'); - var magazineConfig, - parts; - - args.PipelineName = null; - - magazineConfig = StylaMain.GetConfigForAlias(args.RedirectOrigin); - - if (magazineConfig) { - // read pipeline name from configuration - parts = magazineConfig.pipeline.split('-'); - if (parts.length === 2) { - args.PipelineName = magazineConfig.pipeline; - } - else if (parts.length === 3) { - // strip leading cartridge name - args.PipelineName = parts[1] + '-' + parts[2]; - } - else { - args.PipelineName = null; - var pip = magazineConfig.pipeline; - if (typeof magazineConfig.pipeline === 'undefined') { - pip = '(undefined)'; - } - else if (magazineConfig.pipeline === null) { - pip = '(null)'; - } - Logger.error('invalid pipeline specified: "{0}"', pip); - } - - if (args.PipelineName) { - request.custom.MagazineConfiguration = magazineConfig; - } - } - else { - Logger.debug('no matching config found for path: ' + args.RedirectOrigin); - } - - return PIPELET_NEXT; -} diff --git a/cartridges/int_styla/cartridge/js/styla/cart.js b/cartridges/int_styla/cartridge/static/default/js/cart.js similarity index 100% rename from cartridges/int_styla/cartridge/js/styla/cart.js rename to cartridges/int_styla/cartridge/static/default/js/cart.js diff --git a/cartridges/int_styla/cartridge/static/default/js/cart.min.js b/cartridges/int_styla/cartridge/static/default/js/cart.min.js new file mode 100644 index 0000000..e9363c6 --- /dev/null +++ b/cartridges/int_styla/cartridge/static/default/js/cart.min.js @@ -0,0 +1 @@ +"use strict";var addToCart=function(a,t){return new Promise(function(r,n){$.ajax({type:"POST",url:$(".stylaAddToCartUrl").val()+"?format=ajax",data:{pid:a,quantity:t}}).done(function(a){$(".minicart").trigger("count:update",a),r(a)}).fail(function(a,t,r){n(r||"error")})})};module.exports=function(){window.styla=window.styla||{callbacks:[]},styla&&styla.callbacks&&styla.callbacks.push({addToCart:addToCart})};"use strict";var addToCart=function(a,t){return new Promise(function(r,l){$.ajax({type:"POST",url:$(".stylaAddToCartUrl").val()+"?format=ajax",data:{pid:a,quantity:t}}).done(function(a){$(".minicart").trigger("count:update",a),r(a)}).fail(function(a,t,r){l(r||"error")})})};window.styla=window.styla||{callbacks:[]},styla&&styla.callbacks&&styla.callbacks.push({addToCart:addToCart}); \ No newline at end of file diff --git a/cartridges/int_styla/cartridge/templates/default/rendering/catlanding.isml b/cartridges/int_styla/cartridge/templates/default/rendering/catlanding.isml index 3be9b32..8b5c970 100755 --- a/cartridges/int_styla/cartridge/templates/default/rendering/catlanding.isml +++ b/cartridges/int_styla/cartridge/templates/default/rendering/catlanding.isml @@ -2,34 +2,34 @@ Example of a category rendering template which can be used to render a Styla magazine. - + - + - + Configured as rendering template for the general category landing page. Displays two category specific slots containing static html with promotional banner in the first slot and either up to four featured products or promotional content in the second slot. - -
- + +
+
- + - +
- + diff --git a/cartridges/int_styla/cartridge/templates/default/styla/bodyinclude.isml b/cartridges/int_styla/cartridge/templates/default/styla/bodyinclude.isml index cb1072f..a07ca8e 100755 --- a/cartridges/int_styla/cartridge/templates/default/styla/bodyinclude.isml +++ b/cartridges/int_styla/cartridge/templates/default/styla/bodyinclude.isml @@ -7,10 +7,10 @@ - var StylaMain = require('/int_styla/cartridge/scripts/StylaMain'); + var StylaMain = require('/int_styla/cartridge/scripts/stylaMain'); StylaMain.SetHttpStatus(); - + diff --git a/cartridges/int_styla/package.json b/cartridges/int_styla/package.json index e6834b3..93eef97 100755 --- a/cartridges/int_styla/package.json +++ b/cartridges/int_styla/package.json @@ -1,6 +1,6 @@ { "init": [ - "./cartridge/scripts/init/StylaServiceInit.js" + "./cartridge/scripts/init/stylaServiceInit.js" ], "cartridgeVersion": "19.0.1" diff --git a/cartridges/int_styla_refArch/cartridge/controllers/RedirectURL.js b/cartridges/int_styla_refArch/cartridge/controllers/RedirectURL.js new file mode 100644 index 0000000..4a68f67 --- /dev/null +++ b/cartridges/int_styla_refArch/cartridge/controllers/RedirectURL.js @@ -0,0 +1,30 @@ +'use strict'; + +var server = require('server'); + +server.extend(module.superModule); + +server.append('Start', function (req, res, next) { + var URLRedirectMgr = require('dw/web/URLRedirectMgr'); + + var redirect = URLRedirectMgr.redirect; + var location = redirect ? redirect.location : null; + + if (!location) { + var StylaMain, + path = URLRedirectMgr.redirectOrigin, + handledByStyla = false; + if (dw.system.Site.current.getCustomPreferenceValue('stylaEnabled') == true) { + StylaMain = require('/int_styla_refArch/cartridge/scripts/stylaMain'); + handledByStyla = StylaMain.Alias(path); + } + + if (handledByStyla) { + return handledByStyla; + } + } + + next(); +}); + +module.exports = server.exports(); \ No newline at end of file diff --git a/cartridges/int_styla_refArch/cartridge/controllers/StylaMagazine.js b/cartridges/int_styla_refArch/cartridge/controllers/StylaMagazine.js index dac473e..55c653b 100755 --- a/cartridges/int_styla_refArch/cartridge/controllers/StylaMagazine.js +++ b/cartridges/int_styla_refArch/cartridge/controllers/StylaMagazine.js @@ -13,7 +13,8 @@ var server = require('server'); * */ -var StylaMain = require('/int_styla_refArch/cartridge/scripts/StylaMain'); +var StylaMain = require('/int_styla_refArch/cartridge/scripts/stylaMain'); +var cache = require('*/cartridge/scripts/middleware/cache'); function renderContent(template, res) { var pdict = StylaMain.GetRenderContent(); @@ -24,12 +25,12 @@ function renderContent(template, res) { } } -server.get('HeaderContent', function (req, res, next) { +server.get('HeaderContent', cache.applyStylaCustomCache, function (req, res, next) { renderContent('styla/headercontent', res); return next(); }); -server.get('BodyContent', function (req, res, next) { +server.get('BodyContent', cache.applyStylaCustomCache, function (req, res, next) { renderContent('styla/bodycontent', res); return next(); }); diff --git a/cartridges/int_styla_refArch/cartridge/scripts/StylaMain.js b/cartridges/int_styla_refArch/cartridge/scripts/StylaMain.js index cb67b7e..7de2177 100755 --- a/cartridges/int_styla_refArch/cartridge/scripts/StylaMain.js +++ b/cartridges/int_styla_refArch/cartridge/scripts/StylaMain.js @@ -7,15 +7,15 @@ */ var CustomObjectMgr = require('dw/object/CustomObjectMgr'); -var Logger = require('dw/system/Logger').getLogger('styla', 'StylaMain'); -var StylaServiceInit = require('./init/StylaServiceInit'); +var Logger = require('dw/system/Logger').getLogger('styla', 'stylaMain'); +var StylaServiceInit = require('./init/stylaServiceInit'); var Site = require('dw/system/Site'); var CONFIG_CO_TYPE = 'StylaMagazineConfiguration'; // custom object type for storing magazine configurations var CONFIG_CO_KEY_ATTR = 'Key_and_Sort_Order'; // name of the custom object's key attribute var CONFIG_CO_SORT_ORRDER = 'custom.' + CONFIG_CO_KEY_ATTR + ' asc'; // sort order of custom objects -var STYLA_ENABLED = Site.current.getCustomPreferenceValue('stylaEnabled') == true; +var STYLA_ENABLED = Site.getCurrent().getCustomPreferenceValue('stylaEnabled') == true; /** @@ -217,7 +217,7 @@ function getMagazineConfiguration(path) { } } } - iter.close(); + // iter.close(); if (!skipHomepageIter) { iter2 = CustomObjectMgr.queryCustomObjects(CONFIG_CO_TYPE, 'custom.enabled = true', CONFIG_CO_SORT_ORRDER, null); while (iter2.hasNext()) { @@ -235,7 +235,7 @@ function getMagazineConfiguration(path) { } } } - iter2.close(); + // iter2.close(); } } // path not empty @@ -259,7 +259,6 @@ function getConfigForAlias(path) { result = magazineConfig; } } - return result; } diff --git a/cartridges/int_styla_refArch/cartridge/scripts/helpers/searchHelpers.js b/cartridges/int_styla_refArch/cartridge/scripts/helpers/searchHelpers.js new file mode 100644 index 0000000..a85da54 --- /dev/null +++ b/cartridges/int_styla_refArch/cartridge/scripts/helpers/searchHelpers.js @@ -0,0 +1,127 @@ +'use strict'; + +var base = module.superModule || module.parent.superModule; //added parent to module for testing purposes only. It does not interfere with the functionality of the rest of the app + +function setupSearch(apiProductSearch, params) { + var CatalogMgr = require('dw/catalog/CatalogMgr'); + var searchModelHelper = require('*/cartridge/scripts/search/search'); + + var cgid = null; + if (params.cgid) { + cgid = params.cgid; + } else if ('MagazineConfiguration' in request.custom && request.custom.MagazineConfiguration && request.custom.MagazineConfiguration.categoryID) { + // found category ID stored by StylaMagazine.alias() + cgid = request.custom.MagazineConfiguration.categoryID; + } + if (cgid) { + var sortingRule = params.srule ? CatalogMgr.getSortingRule(params.srule) : null; + var selectedCategory = CatalogMgr.getCategory(cgid); + selectedCategory = selectedCategory && selectedCategory.online ? selectedCategory : null; + + searchModelHelper.setProductProperties(apiProductSearch, params, selectedCategory, sortingRule); + + if (params.preferences) { + searchModelHelper.addRefinementValues(apiProductSearch, params.preferences); + } + } + + return apiProductSearch; +} + +function search(req, res) { + var CatalogMgr = require('dw/catalog/CatalogMgr'); + var ProductSearchModel = require('dw/catalog/ProductSearchModel'); + var URLUtils = require('dw/web/URLUtils'); + var pageMetaHelper = require('*/cartridge/scripts/helpers/pageMetaHelper'); + var ProductSearch = require('*/cartridge/models/search/productSearch'); + var reportingUrlsHelper = require('*/cartridge/scripts/reportingUrls'); + var schemaHelper = require('*/cartridge/scripts/helpers/structuredDataHelper'); + + var apiProductSearch = new ProductSearchModel(); + var categoryTemplate = ''; + var maxSlots = 4; + var productSearch; + var reportingURLs; + + var searchRedirect = req.querystring.q ? apiProductSearch.getSearchRedirect(req.querystring.q) : null; + + if (searchRedirect) { + return { searchRedirect: searchRedirect.getLocation() }; + } + + apiProductSearch = setupSearch(apiProductSearch, req.querystring); + apiProductSearch.search(); + + if (!apiProductSearch.personalizedSort) { + base.applyCache(res); + } + categoryTemplate = base.getCategoryTemplate(apiProductSearch); + productSearch = new ProductSearch( + apiProductSearch, + req.querystring, + req.querystring.srule, + CatalogMgr.getSortingOptions(), + CatalogMgr.getSiteCatalog().getRoot() + ); + + pageMetaHelper.setPageMetaTags(req.pageMetaData, productSearch); + + var canonicalUrl = URLUtils.url('Search-Show', 'cgid', req.querystring.cgid); + var refineurl = URLUtils.url('Search-Refinebar'); + var whitelistedParams = ['q', 'cgid', 'pmin', 'pmax', 'srule']; + var isRefinedSearch = false; + + Object.keys(req.querystring).forEach(function (element) { + if (whitelistedParams.indexOf(element) > -1) { + refineurl.append(element, req.querystring[element]); + } + + if (['pmin', 'pmax'].indexOf(element) > -1) { + isRefinedSearch = true; + } + + if (element === 'preferences') { + var i = 1; + isRefinedSearch = true; + Object.keys(req.querystring[element]).forEach(function (preference) { + refineurl.append('prefn' + i, preference); + refineurl.append('prefv' + i, req.querystring[element][preference]); + i++; + }); + } + }); + + if (productSearch.searchKeywords !== null && !isRefinedSearch) { + reportingURLs = reportingUrlsHelper.getProductSearchReportingURLs(productSearch); + } + + var result = { + productSearch: productSearch, + maxSlots: maxSlots, + reportingURLs: reportingURLs, + refineurl: refineurl, + canonicalUrl: canonicalUrl + }; + + if (productSearch.isCategorySearch && !productSearch.isRefinedCategorySearch && categoryTemplate && apiProductSearch.category.parent.ID === 'root') { + pageMetaHelper.setPageMetaData(req.pageMetaData, productSearch.category); + result.category = apiProductSearch.category; + result.categoryTemplate = categoryTemplate; + } + if ('MagazineConfiguration' in request.custom && request.custom.MagazineConfiguration && request.custom.MagazineConfiguration.categoryID) { + result.category = apiProductSearch.category; + result.categoryTemplate = categoryTemplate; + } + if (!categoryTemplate || categoryTemplate === 'rendering/category/categoryproducthits') { + result.schemaData = schemaHelper.getListingPageSchema(productSearch.productIds); + } + + // console.log(result); + return result; +} + +exports.setupSearch = setupSearch; +exports.getCategoryTemplate = base.getCategoryTemplate; +exports.setupContentSearch = base.setupContentSearch; +exports.search = search; +exports.applyCache = base.applyCache; \ No newline at end of file diff --git a/cartridges/int_styla_refArch/cartridge/scripts/init/StylaServiceInit.js b/cartridges/int_styla_refArch/cartridge/scripts/init/StylaServiceInit.js index f95cb08..a4b7b2c 100755 --- a/cartridges/int_styla_refArch/cartridge/scripts/init/StylaServiceInit.js +++ b/cartridges/int_styla_refArch/cartridge/scripts/init/StylaServiceInit.js @@ -2,7 +2,7 @@ var LocalServiceRegistry = require('dw/svc/LocalServiceRegistry'); -var System = require('dw/system/System'); +// var System = require('dw/system/System'); /** * Service used for fetching SEO content for a specific URL. @@ -43,6 +43,10 @@ module.exports.StylaSeoContentHttpService = LocalServiceRegistry.createService(' body: '' } }; + }, + + filterLogMessage: function (msg) { + return msg; } }); @@ -84,6 +88,10 @@ module.exports.StylaVersionService = LocalServiceRegistry.createService('StylaVe error: false, version: 'x' }; + }, + + filterLogMessage: function (msg) { + return msg; } }); diff --git a/cartridges/int_styla_refArch/cartridge/scripts/middleware/cache.js b/cartridges/int_styla_refArch/cartridge/scripts/middleware/cache.js new file mode 100644 index 0000000..8de32b9 --- /dev/null +++ b/cartridges/int_styla_refArch/cartridge/scripts/middleware/cache.js @@ -0,0 +1,23 @@ +'use strict'; + +var base = module.superModule || module.parent.superModule; //added parent to module for testing purposes only. It does not interfere with the functionality of the rest of the app + +function applyStylaCustomCache(req, res, next) { + var Site = require('dw/system/Site'); + var site = Site.getCurrent(); + let cachePeriod = site.getCustomPreferenceValue('stylaSeoCachingTTL'); + + res.cachePeriod = cachePeriod; // eslint-disable-line no-param-reassign + res.cachePeriodUnit = 'minutes'; // eslint-disable-line no-param-reassign + res.personalized = true; // eslint-disable-line no-param-reassign + + next(); +} + +module.exports = { + applyDefaultCache: base.applyDefaultCache, + applyPromotionSensitiveCache: base.applyPromotionSensitiveCache, + applyInventorySensitiveCache: base.applyInventorySensitiveCache, + applyShortPromotionSensitiveCache: base.applyShortPromotionSensitiveCache, + applyStylaCustomCache: applyStylaCustomCache +} \ No newline at end of file diff --git a/cartridges/int_styla_refArch/cartridge/scripts/pipelets/GetAliasPipeline.js b/cartridges/int_styla_refArch/cartridge/scripts/pipelets/GetAliasPipeline.js deleted file mode 100755 index c960495..0000000 --- a/cartridges/int_styla_refArch/cartridge/scripts/pipelets/GetAliasPipeline.js +++ /dev/null @@ -1,51 +0,0 @@ -/** -* If the current URL is part of a magazine, then read the pipeline name from the corresponding magazine configuration. -* -* @input RedirectOrigin : String Original URL before redirect. -* @output PipelineName : String Pipeline to redirect to. Null if no matching magazine configuration was found for the current request path. -* -*/ - -function execute(args : PipelineDictionary) : Number -{ - var Logger = require('dw/system/Logger').getLogger('styla', 'GetAliasPipeline'); - var StylaMain = require('/int_styla_refArch/cartridge/scripts/StylaMain'); - var magazineConfig, - parts; - - args.PipelineName = null; - - magazineConfig = StylaMain.GetConfigForAlias(args.RedirectOrigin); - - if (magazineConfig) { - // read pipeline name from configuration - parts = magazineConfig.pipeline.split('-'); - if (parts.length === 2) { - args.PipelineName = magazineConfig.pipeline; - } - else if (parts.length === 3) { - // strip leading cartridge name - args.PipelineName = parts[1] + '-' + parts[2]; - } - else { - args.PipelineName = null; - var pip = magazineConfig.pipeline; - if (typeof magazineConfig.pipeline === 'undefined') { - pip = '(undefined)'; - } - else if (magazineConfig.pipeline === null) { - pip = '(null)'; - } - Logger.error('invalid pipeline specified: "{0}"', pip); - } - - if (args.PipelineName) { - request.custom.MagazineConfiguration = magazineConfig; - } - } - else { - Logger.debug('no matching config found for path: ' + args.RedirectOrigin); - } - - return PIPELET_NEXT; -} diff --git a/cartridges/int_styla_refArch/cartridge/js/styla/cart.js b/cartridges/int_styla_refArch/cartridge/static/default/js/cart.js similarity index 100% rename from cartridges/int_styla_refArch/cartridge/js/styla/cart.js rename to cartridges/int_styla_refArch/cartridge/static/default/js/cart.js diff --git a/cartridges/int_styla_refArch/cartridge/static/default/js/cart.min.js b/cartridges/int_styla_refArch/cartridge/static/default/js/cart.min.js new file mode 100644 index 0000000..f2ace95 --- /dev/null +++ b/cartridges/int_styla_refArch/cartridge/static/default/js/cart.min.js @@ -0,0 +1 @@ +"use strict";var addToCart=function(a,t){return new Promise(function(r,n){$.ajax({type:"POST",url:$(".stylaAddToCartUrl").val()+"?format=ajax",data:{pid:a,quantity:t}}).done(function(a){$(".minicart").trigger("count:update",a),r(a)}).fail(function(a,t,r){n(r||"error")})})};module.exports=function(){window.styla=window.styla||{callbacks:[]},styla&&styla.callbacks&&styla.callbacks.push({addToCart:addToCart})}; \ No newline at end of file diff --git a/cartridges/int_styla_refArch/cartridge/templates/default/styla/bodycontent.isml b/cartridges/int_styla_refArch/cartridge/templates/default/styla/bodycontent.isml index a2f2dbf..068781e 100755 --- a/cartridges/int_styla_refArch/cartridge/templates/default/styla/bodycontent.isml +++ b/cartridges/int_styla_refArch/cartridge/templates/default/styla/bodycontent.isml @@ -4,9 +4,6 @@ - - - diff --git a/cartridges/int_styla_refArch/cartridge/templates/default/styla/bodyinclude.isml b/cartridges/int_styla_refArch/cartridge/templates/default/styla/bodyinclude.isml index f1242c9..3633af5 100755 --- a/cartridges/int_styla_refArch/cartridge/templates/default/styla/bodyinclude.isml +++ b/cartridges/int_styla_refArch/cartridge/templates/default/styla/bodyinclude.isml @@ -7,10 +7,10 @@ - var StylaMain = require('/int_styla_refArch/cartridge/scripts/StylaMain'); + var StylaMain = require('/int_styla_refArch/cartridge/scripts/stylaMain'); StylaMain.SetHttpStatus(); - + diff --git a/cartridges/int_styla_refArch/cartridge/templates/default/styla/headercontent.isml b/cartridges/int_styla_refArch/cartridge/templates/default/styla/headercontent.isml index 89c907f..630665e 100755 --- a/cartridges/int_styla_refArch/cartridge/templates/default/styla/headercontent.isml +++ b/cartridges/int_styla_refArch/cartridge/templates/default/styla/headercontent.isml @@ -5,10 +5,7 @@ - - - - + diff --git a/cartridges/int_styla_refArch/package.json b/cartridges/int_styla_refArch/package.json index e6834b3..93eef97 100755 --- a/cartridges/int_styla_refArch/package.json +++ b/cartridges/int_styla_refArch/package.json @@ -1,6 +1,6 @@ { "init": [ - "./cartridge/scripts/init/StylaServiceInit.js" + "./cartridge/scripts/init/stylaServiceInit.js" ], "cartridgeVersion": "19.0.1" diff --git a/documentation/Integration Guide.html b/documentation/Integration Guide.html index 18fbbb2..8519538 100755 --- a/documentation/Integration Guide.html +++ b/documentation/Integration Guide.html @@ -2,9 +2,9 @@ - +

int_styla

-

Version 19.0.1

+

Version 20.1.0

Table of Contents

  1. Summary
  2. @@ -129,9 +129,10 @@

    Limitations & Constraints

    For example, the magazine base path cannot contain nested folders like /fashion/magazine when using pipelines because a pipeline alias only supports direct folders like /magazine.

    -

    Compatiblity

    -

    The Styla implementation cartridge is available since Commerce Cloud 15.6.

    +

    Compatibility

    +

    The Styla implementation cartridge is available since Commerce Cloud 15.6 and is compatible with all recent versions of Commerce Cloud.

    Site Genesis 16.1 has been used as reference for this installation guide.

    +

    The cartridge is compatible with any locale requested by the client.

    This cartridge supports both integrating with a storefront based on controllers as well as one that is using pipelines.

    @@ -155,11 +156,10 @@

    Cartridge Path

    Styla cartridge path

    Site Preferences and Custom Objects

    -
    Import metadata definitions
    -

    New site preferences and a custom object definition need to be imported into the Commerce Cloud instance. - Go to Administration > Site Development > Import & Export and upload the file - metadata/styla_magazine_metadata.xml. - Then, import it as Meta Data. +

    Import metadata definitions and services
    +

    New site preferences, custom object definition and services need to be imported into the Commerce Cloud instance. + Go to Administration > Site Development > Site Import & Export > Import Section and upload the file + metadata/Global Data.zip. Then, import it.

    Site Preferences

    When this is finished, go to Merchant Tools > Site Preferences > Custom Site Preferences > @@ -302,12 +302,9 @@

    Custom Objects for Magazine confi -

    Service Configuration

    -

    Styla loads SEO data into the page using a Web Service, which needs to be configured. The service configurations - can be imported like this: Go to Administration > Operations > Import & Export - and upload metadata/styla_magazine_services.xml. After successfully importing this file, - go to Administration > Operations > Services and check if the configuration is according to the following - screen shots. If so, no further adjustments are required. +

    Service Configuration Check

    +

    After successfully importing the file, go to Administration > Operations > Services and check if the configuration is according + to the following screen shots. If so, no further adjustments are required.

    Service Profile @@ -391,7 +388,6 @@

    Magazine

    catchall

    You can still get the same behavior if you manually redirect to the target of your catch-all rule. For an example see the code comment in the chapter RedirectURL.js. - For pipelines see the chapter RedirectURL pipeline.

    Category rendering template

    When using category URLs you need to specify a rendering template for the category. The cartridge includes @@ -520,6 +516,36 @@

    homepage.isml (example)
    <isset name="SkipPageTitleAndMeta" value="${true}" scope="page"/>
     <isdecorate template="content/home/pt_storefront">
     
    +
    footer_UI.isml
    +

    If product information and add to cart functionality is used by the Styla magazine, a JavaScript function + needs to be exposed that can be called by Styla if a user adds a product to the cart. For SiteGenesis this is achieved + by adding the provided cart.min.js path to the shop's footer_UI.isml file. Thus, add the following line to footer_UI.isml + (after the inclusion of app.js file): +

    +
    <script src="${URLUtils.staticURL('/js/cart.min.js')}"></script>
    +

    The result should look like this:

    +
    +         
    +
    +<iscomment>third-party add-ons</iscomment>
    +<script src="${URLUtils.staticURL('/lib/jquery/jquery.jcarousel.min.js')}" type="text/javascript"></script>
    +<script src="${URLUtils.staticURL('/lib/jquery/jquery.validate.min.js')}" type="text/javascript"></script>
    +<script src="${URLUtils.staticURL('/lib/jquery/jquery.zoom.min.js')}"></script>
    +<script type="text/javascript"><isinclude template="resources/appresources"/></script>
    +<script type="text/javascript"><isinclude url="${URLUtils.url('Resources-LoadTransient')}"/></script>
    +<script src="${URLUtils.staticURL('/js/app.js')}"></script>
    +<script src="${URLUtils.staticURL('/js/cart.min.js')}"></script>
    +<isif condition="${!('pageContext' in this) || empty(pageContext)}">
    +   <isscript>pageContext = new Object();</isscript>
    +</isif>
    +<script>pageContext = <isprint value="${JSON.stringify(pageContext)}" encoding="off"/>;</script>
    +<script>
    +   var meta = "${pdict.CurrentPageMetaData.description}";
    +   var keywords = "${pdict.CurrentPageMetaData.keywords}";
    +</script>
    +         
    +      
    +

    The cart.min.js then takes care of exposing and registering the add to cart functionality with Styla.

    URL Handling

    RedirectURL.js

    The following step is required if your storefront uses controllers. You can skip this if you are using pipelines.

    @@ -583,38 +609,6 @@
    RedirectURL.js
    Since the URL in the customer's browser is still magazine/stories/5 the Styla JavaScript will load the corresponding magazine content.

    -
    RedirectURL pipeline
    -

    The following step is required if your storefront uses pipelines. You can skip this if you are using controllers.

    -

    The RedirectURL-Start pipeline needs to be changed: In the error exit edge of the RedirectURL pipelet - insert the following: -

    -
      -
    • A call node to StylaMagazine-Alias
    • -
    • A decision node with Decision Key set to empty(PipelineName)
    • -
    • A dynamic jump node which reads its target from the variable PipelineName
    • -
    -

    Original pipeline: - redirect_pipeline_1 -

    -

    Changed pipeline: - redirect_pipeline_2 -

    -

    If you prefer the error behavior of the catch-all redirect rule then instead of rendering the - util/redirecterror template you can use a jump node with target Home-ErrorNotFound: -

    -

    Home-ErrorNotFound pipeline instead of redirecterror template: - redirect_pipeline_3 -

    -

    The Redirect-Start pipelines shown above require that the int_styla cartridge is in the cartridge path - of all sites which use this Redirect-Start pipeline. If you want to avoid that then you can insert a check - for the site preference stylaEnabled before the StylaMagazine-Alias pipeline is called. -

    -

    Check if Styla is enabled before calling the StylaMagazine-Alias pipeline: - redirect_pipeline_4 -

    -

    See int_styla/cartridge/example/pipelines/RedirectURL.xml for an example, you can copy the - pipeline modifications from there. -

    SearchModel.js

    The following step is required if your storefront uses controllers. You can skip this if you are using pipelines.

    If using a category URL, the SearchModel.initializeProductSearchModel controller method has to be @@ -648,7 +642,7 @@

    SearchModel.js
    if (!empty(cgid)) { var category = CatalogMgr.getCategory(cgid); if (category && category.isOnline() && productSearchModel) { - productSearchModel.setCategoryID(category.getID()); + productSearchModel.setCategoryID(category.ge`tID()); } } @@ -660,49 +654,6 @@
    SearchModel.js
    /int_styla/cartridge/templates/default/rendering/catlanding.isml or create a custom one as described in chapter Magazine Inclusion.

    -
    Search pipeline
    -

    The following step is required if your storefront uses pipelines. You can skip this if you are using controllers.

    -

    The Search-Show pipeline needs to be changed, specifically modify the CategoryID parameter of the - Search pipelet: -

    -

    Old value:

    -
    CurrentHttpParameterMap.cgid.value
    -
    -

    New value:

    -
    CurrentHttpParameterMap.cgid.value || ('MagazineConfiguration' in CurrentRequest.custom && !empty(CurrentRequest.custom.MagazineConfiguration) ? CurrentRequest.custom.MagazineConfiguration.categoryID : null)
    -
    -

    Search-Show pipeline: - search_pipeline_1 -

    -

    What this change does: If the redirect handler has determined that the current request's URL matches a magazine - configuration, and if that configuration pointed to the Search-Show pipeline, then this change will cause the - Search pipelet to use the category ID which is specified in the magazine configuration. -

    -

    JavaScript Actions

    -
    app.js
    -

    If product information and add to cart functionality is used by the Styla magazine, a JavaScript function - needs to be exposed that can be called by Styla if a user adds a product to the cart. This is achieved - by adding the provided cart.js to the shop's app.js file. Thus, add the following line to app.js - (somewhere near the top, after minicart has been defined): -

    -
    require('../../../int_styla/cartridge/js/styla/cart.js')(minicart);
    -
    -

    The result should look like this:

    -
    var countries = require('./countries'),
    -    dialog = require('./dialog'),
    -    minicart = require('./minicart'),
    -    page = require('./page'),
    -    rating = require('./rating'),
    -    searchplaceholder = require('./searchplaceholder'),
    -    searchsuggest = require('./searchsuggest'),
    -    searchsuggestbeta = require('./searchsuggest-beta'),
    -    tooltip = require('./tooltip'),
    -    util = require('./util'),
    -    validator = require('./validator');
    -
    -require('../../../int_styla/cartridge/js/styla/cart.js')(minicart);
    -
    -

    The cart.js then takes care of exposing and registering the add to cart functionality with Styla.

    Estimated Effort

    The estimated effort to make the necessary changes to the shop cartridge is 8-12 hours, depending on the features being used (Styla SEO, add to cart feature, multiple magazines, etc.). @@ -858,11 +809,10 @@

    Cartridge Path

    Styla cartridge path

    Site Preferences and Custom Objects

    -
    Import metadata definitions
    -

    New site preferences and a custom object definition need to be imported into the Commerce Cloud instance. - Go to Administration > Site Development > Import & Export and upload the file - metadata/styla_magazine_metadata.xml. - Then, import it as Meta Data. +

    Import metadata definitions and services
    +

    New site preferences, custom object definition and services need to be imported into the Commerce Cloud instance. + Go to Administration > Site Development > Site Import & Export > Import Section and upload the file + metadata/Global Data.zip. Then, import it.

    Site Preferences

    When this is finished, go to Merchant Tools > Site Preferences > Custom Site Preferences > @@ -1005,12 +955,9 @@

    Custom Objects for Magazine confi -

    Service Configuration

    -

    Styla loads SEO data into the page using a Web Service, which needs to be configured. The service configurations - can be imported like this: Go to Administration > Operations > Import & Export - and upload metadata/styla_magazine_services.xml. After successfully importing this file, - go to Administration > Operations > Services and check if the configuration is according to the following - screen shots. If so, no further adjustments are required. +

    Service Configuration Check

    +

    After successfully importing the file, go to Administration > Operations > Services and check if the configuration is according + to the following screen shots. If so, no further adjustments are required.

    Service Profile @@ -1094,7 +1041,6 @@

    Magazine

    catchall

    You can still get the same behavior if you manually redirect to the target of your catch-all rule. For an example see the code comment in the chapter RedirectURL.js. - For pipelines see the chapter RedirectURL pipeline.

    Category rendering template

    When using category URLs you need to specify a rendering template for the category. The cartridge includes @@ -1224,153 +1170,22 @@

    homepage.isml (example)
    <isset name="SkipPageTitleAndMeta" value="${true}" scope="page"/>
     <isdecorate template="content/home/pt_storefront">
     
    -

    URL Handling

    -
    RedirectURL.js
    -

    The following step is required if your storefront uses controllers. You can skip this if you are using pipelines.

    -

    In RedirectURL.js the start() function needs to look like this

    - -
    
    -
    -server.get('Start', function (req, res, next) {
    -    var URLRedirectMgr = require('dw/web/URLRedirectMgr');
    -
    -    var redirect = URLRedirectMgr.redirect;
    -    var location = redirect ? redirect.location : null;
    -    var redirectStatus = redirect ? redirect.getStatus() : null;
    -
    -    if (!location) {
    -        var StylaMain,
    -            path = URLRedirectMgr.redirectOrigin,
    -            handledByStyla = false;
    -        if (dw.system.Site.current.getCustomPreferenceValue('stylaEnabled') == true) {
    -            StylaMain = require('/int_styla_refArch/cartridge/scripts/StylaMain');
    -            return StylaMain.Alias(path);
    -        }
    -        if (!handledByStyla) {
    -            res.setStatusCode(404);
    -            res.render('error/notFound');
    -        }
    -    } else {
    -        if (redirectStatus) {
    -            res.setRedirectStatus(redirectStatus);
    -        }
    -        res.redirect(location);
    -    }
    -
    -    next();
    -});
    -
    -

    Before the change if no matching URL rule was found then the redirecterror template was rendered. - Now if no matching rule is found then StylaMagazine.Alias() is called. If that method finds a magazine - configuration object where the Magazine Base Path matches redirectOrigin then it will - call the controller specified in the magazine configuration; if no matching magazine configuration is - found, then the redirecterror template is rendered. -

    -

    E.g. assume we have a pipeline alias magazine assigned to Home-Show which renders a Styla magazine. - When interacting with the magazine the Styla JavaScript will modify the URL in the - customer's browser to e.g. magazine/stories/5. - If a customer navigates to this URL then RedirectUrl.start() doesn't find a matching rule for - magazine/stories/5 and so it calls StylaMagazine.Alias(). - If there is a magazine configuration in site preferences with Magazine Base Path magazine and - Magazine Controller Method set to app_storefront_controllers-Home-Show then StylaMagazine.Alias() - will execute the Home.Show() controller method and return true. - Since the URL in the customer's browser is still magazine/stories/5 the Styla JavaScript will load - the corresponding magazine content. -

    - -
    SearchHelpers.js
    -

    If using a category URL, the searchHelpers.setupSearch and searchHelpers.search methods have to be - modified. When StylaMagazine.Alias() (see previous section) finds a matching magazine configuration - it will store the Magazine Category ID from the configuration in request.custom.MagazineCategoryId. - The following change allows the search model to read the category ID from request.custom.MagazineCategoryId - if httpParameterMap.cgid is not submitted. -

    -

    - For UNIT TESTING replace searchHelpers.js from the Documentation folder with test/unit/app_storefront_base/scripts/helpers/searchHelpers.js -

    -

    Change the setupSearch method to look like this -

    
    -
    -function setupSearch(apiProductSearch, params) {
    -    var CatalogMgr = require('dw/catalog/CatalogMgr');
    -    var searchModelHelper = require('*/cartridge/scripts/search/search');
    -
    -    var cgid = null;
    -    if (params.cgid) {
    -        cgid = params.cgid;
    -    } else if ('MagazineConfiguration' in request.custom && request.custom.MagazineConfiguration
    -     && request.custom.MagazineConfiguration.categoryID) {
    -        // found category ID stored by StylaMagazine.alias()
    -        cgid = request.custom.MagazineConfiguration.categoryID;
    -    }
    -    if (cgid) {
    -        var sortingRule = params.srule ? CatalogMgr.getSortingRule(params.srule) : null;
    -        var selectedCategory = CatalogMgr.getCategory(cgid);
    -        selectedCategory = selectedCategory && selectedCategory.online ? selectedCategory : null;
    -
    -        searchModelHelper.setProductProperties(apiProductSearch, params, selectedCategory, sortingRule);
    -
    -        if (params.preferences) {
    -            searchModelHelper.addRefinementValues(apiProductSearch, params.preferences);
    -        }
    -    }
    -
    -    return apiProductSearch;
    -}
    -
    -
    -

    Add in the search method before returning the result the following -

    
    -
    -    if ('MagazineConfiguration' in request.custom && request.custom.MagazineConfiguration
    -     && request.custom.MagazineConfiguration.categoryID) {
    -        result.category = apiProductSearch.category;
    -        result.categoryTemplate = categoryTemplate;
    -    }
    -
    -
    -

    The value of the category ID needs to be set in the magazine configuration's custom object attribute - categoryID. Apart from that, - use the category rendering template provided in this cartridge - /int_styla_refArch/cartridge/templates/default/rendering/catlanding.isml or create a custom one as described - in chapter Magazine Inclusion. -

    - -

    JavaScript Actions

    -
    main.js
    +
    scripts.isml

    If product information and add to cart functionality is used by the Styla magazine, a JavaScript function needs to be exposed that can be called by Styla if a user adds a product to the cart. This is achieved - by adding the provided cart.js to the shop's app.js file. Thus, add the following line to app.js - (somewhere near the top, after minicart has been defined): + by adding the provided cart.min.js path to the shop's scripts.isml file. Thus, add the following line to scripts.isml + (after the inclusion of main.js file):

    -
    
    -      	require('../../../../../int_styla_refArch/cartridge/js/styla/cart.js')();
    -
    -

    The result should look like this:

    
    -window.jQuery = window.$ = require('jquery');
    -var processInclude = require('./util');
    -
    -$(document).ready(function () {
    -    processInclude(require('./components/menu'));
    -    processInclude(require('./components/cookie'));
    -    processInclude(require('./components/consentTracking'));
    -    processInclude(require('./components/footer'));
    -    processInclude(require('./components/miniCart'));
    -    processInclude(require('./components/collapsibleItem'));
    -    processInclude(require('./components/search'));
    -    processInclude(require('./components/clientSideValidation'));
    -    processInclude(require('./components/countrySelector'));
    -    processInclude(require('./components/toolTip'));
    -});
    -
    -require('../../../../../int_styla_refArch/cartridge/js/styla/cart.js')();
    -require('./thirdParty/bootstrap');
    -require('./components/spinner');
    -
    +<script>//common/scripts.isml</script>
    +<script src="${URLUtils.staticURL('/js/app.js')}"></script>
    +<script src="${URLUtils.staticURL('/js/cart.min.js')}"></script>
    +<isloop items="${ require('*/cartridge/scripts/assets.js').scripts }" var="script">
    +   <script defer type="text/javascript" src="${script}"></script>
    +</isloop>
     
    -

    The cart.js then takes care of exposing and registering the add to cart functionality with Styla.

    +

    The cart.min.js then takes care of exposing and registering the add to cart functionality with Styla.

    Estimated Effort

    The estimated effort to make the necessary changes to the shop cartridge is 8-12 hours, depending on the features being used (Styla SEO, add to cart feature, multiple magazines, etc.). diff --git a/documentation/images/intguide/flow_add_product_to_cart.png b/documentation/images/intguide/flow_add_product_to_cart.png index 06eb60c..163e2a6 100755 Binary files a/documentation/images/intguide/flow_add_product_to_cart.png and b/documentation/images/intguide/flow_add_product_to_cart.png differ diff --git a/documentation/images/intguide/flow_add_product_to_story.png b/documentation/images/intguide/flow_add_product_to_story.png index b01fd1a..2e76778 100755 Binary files a/documentation/images/intguide/flow_add_product_to_story.png and b/documentation/images/intguide/flow_add_product_to_story.png differ diff --git a/documentation/images/intguide/flow_magazine_page_generation.png b/documentation/images/intguide/flow_magazine_page_generation.png index ec75b2e..6dda088 100755 Binary files a/documentation/images/intguide/flow_magazine_page_generation.png and b/documentation/images/intguide/flow_magazine_page_generation.png differ diff --git a/documentation/images/intguide/flow_show_product_details.png b/documentation/images/intguide/flow_show_product_details.png index e446d16..977aed5 100755 Binary files a/documentation/images/intguide/flow_show_product_details.png and b/documentation/images/intguide/flow_show_product_details.png differ diff --git a/metadata /Global Data.zip b/metadata /Global Data.zip new file mode 100644 index 0000000..ab264a6 Binary files /dev/null and b/metadata /Global Data.zip differ diff --git a/metadata/styla_magazine_example_config.xml b/metadata/styla_magazine_example_config.xml deleted file mode 100755 index 274b7d5..0000000 --- a/metadata/styla_magazine_example_config.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - fashion/magazine - magazine - true - app_storefront_controllers-Search-Show - demandwaretest1 - - - - magazine - true - app_storefront_controllers-Home-Show - demandwaretest1 - - - diff --git a/metadata/styla_magazine_metadata.xml b/metadata/styla_magazine_metadata.xml deleted file mode 100755 index d3b95f7..0000000 --- a/metadata/styla_magazine_metadata.xml +++ /dev/null @@ -1,146 +0,0 @@ - - - - - - - Styla Enabled - Determines whether the Styla integration is active for this site or not. - boolean - false - false - false - - - CDN Base URL - Base URL to Styla CDN from where client-side JavaScript is loaded. The placeholder {USER} will be replaced with the Username attribute value of the respective magazine configuration. {VERSION} will be replaced with the response from the Version Service call. - string - false - false - 0 - //client-scripts.styla.com/scripts/clients/{USER}.js - - - CSS Base URL - Base URL of Styla Magazine CSS. The placeholder {USER} will be replaced with the Username site preference value of the respective magazine configuration. {VERSION} will be replaced with the response from the Version Service call. - string - false - false - 0 - //client-scripts.styla.com/styles/clients/{USER}.css - - - SEO Caching TTL - Caching Time for SEO Content in Minutes - int - false - false - Minutes - 60 - - - SEO Disabled - Do not embed SEO content into page - boolean - false - false - false - - - - - Styla Magazine - - - - - - - - - - - Styla Magazine Configuration - Each Custom Object represents one Magazine Configuration. Key_and_Sort_Order: Use this to give a descriptive name to the configuration. When the Styla cartridge searches for a matching configuration, it will go through the custom objects in order, alphabetically sorted by this attribute. A matching configuration is considered found if the request's path contains the Base Path of the configuration and if the request's locale matches the Allowed Locales attribute. - source-to-target - site - - string - 0 - - - - Base Path - URL Path of the Styla Magazine. A matching configuration is considered found if the request's path contains the Base Path of the configuration and if the request's locale matches the Allowed Locales attribute. - string - false - true - false - 0 - - - Category ID - Necessary when using the Search controller (or pipeline), which typically is app_storefront_controllers-Search-Show (or just Search-Show when using pipelines). Make sure this category has at least one product assigned to it, otherwise the category won't be part of the search result. - string - false - false - false - 0 - - - Enabled - Use this to disable a configuration without having to delete the custom object. - boolean - true - false - - - HomePage - Check this box and the Base Path will be ignored. The Magazine will show up on the homepage. - boolean - false - false - - - Allowed Locales - A list of locale identifiers. A magazine configuration is only considered a match if the current request's path contains the Base Path of the configuration and if the request's locale is found in this list. Leave empty to allow all locales. Example entries: de_DE, fr_CH. - set-of-string - false - false - false - - - Controller Method - Specify the Magazine Controller Method in this format: cartridge-controller-method. E.g.: app_storefront_controllers-Home-Show. If your storefront code uses pipelines then you can specify a pipeline here, e.g. Home-Show. If you are using pipelines then the cartridge name can be omitted. - string - false - true - false - 0 - - - Username - Styla user name for this magazine. - string - false - true - false - 0 - - - - - Styla Magazine Configuration - - - - - - - - - - - - - diff --git a/metadata/styla_magazine_services.xml b/metadata/styla_magazine_services.xml deleted file mode 100755 index c2316b4..0000000 --- a/metadata/styla_magazine_services.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - http://seo.styla.com/clients/ - - - - - live.styla.com/api/version/ - - - - - 10000 - false - 0 - 0 - true - 3 - 60000 - - - - 10000 - false - 0 - 0 - true - 3 - 60000 - - - - HTTP - true - styla - false - false - StylaSeoContentProfile - StylaSeoContentCredentials - - - - HTTP - true - styla - false - false - StylaVersionProfile - StylaVersionCredentials - - - diff --git a/test/mocks/dw/experience/PageMgr.js b/test/mocks/dw/experience/PageMgr.js new file mode 100644 index 0000000..53e0b11 --- /dev/null +++ b/test/mocks/dw/experience/PageMgr.js @@ -0,0 +1,7 @@ +var PageMgr = function(){}; + +PageMgr.getPage = function(){return null;}; +PageMgr.renderPage = function(){return null;}; +PageMgr.renderRegion = function(){return null;}; + +module.exports = PageMgr; \ No newline at end of file diff --git a/test/mocks/dw/object/CustomObjectMgr.js b/test/mocks/dw/object/CustomObjectMgr.js new file mode 100644 index 0000000..336f916 --- /dev/null +++ b/test/mocks/dw/object/CustomObjectMgr.js @@ -0,0 +1,116 @@ +var fs = require('fs'); +var CustomObjectMgr = function(){}; + +CustomObjectMgr.removea = function(){}; +CustomObjectMgr.describe = function(){}; +CustomObjectMgr.createCustomObject = function(){}; +CustomObjectMgr.getCustomObject = function(type, id){ + //console.log('CO query for '+type+' with ID '+id); + + var CoClass = require('./CustomObject'); + var customObject = new CoClass(); + + // var tmp = fs.readFileSync('working/'+type+'_CustomObject.xml',{encoding:'utf8'}).split('object-id="'+id+'">\n'); + // if(tmp.length > 1){ + // tmp.split('')[0].split('\n').forEach(function(line){ + // console.log(line); + // }); + // }// else not found + + if (type == "WebserviceCredentials") { + customObject.custom = new Object(); + customObject.custom.url = "http://dummy.com"; + customObject.custom.credentialsType = "DUMMY_VALUES"; + customObject.custom.password = "1234567"; + } + if (type == "WebserviceConfiguration") { + customObject.custom = new Object(); + customObject.custom.targetEnvironment = "Test-Test"; + customObject.custom.timeout = 1000; + customObject.custom.serviceEnableLocking = false; + customObject.custom.logSoap = true; + customObject.custom.logStatus = true; + } + return customObject; + +}; +CustomObjectMgr.getAllCustomObjects = function(){}; +CustomObjectMgr.queryCustomObjects = function () { + var Iterator = require('../util/Iterator'); + var array = [ + { + custom: { + basePath: 'content', + categoryID: 'magazine', + enabled: true, + homepage: false, + Key_and_Sort_Order: '005 cat /fashion/magazine en', + locales: ['de_DE', 'default'], + pipeline: 'app_storefront_base-Search-Show', + username: 'flymo-uk' + }, + type: 'StylaMagazineConfiguration', + UUID: 'd9521b535abe343b1ed4d3d962' + }, + { + custom: { + basePath: 'magazine', + categoryID: 'magazine', + enabled: true, + homepage: false, + Key_and_Sort_Order: '001 cat /fashion/magazine en', + locales: ['de_DE', 'default', 'en_US'], + pipeline: 'app_storefront_base-Search-Show', + username: 'demandwaretest1' + }, + type: 'StylaMagazineConfiguration', + UUID: 'a2d122c102f3710ef42c846208' + }, + { + custom: { + basePath: 'magazine3', + enabled: true, + homepage: false, + Key_and_Sort_Order: '001 gome /fashion/magazine en', + locales: ['en_GB', 'en_US'], + pipeline: 'app_storefront_base-Home-Show', + username: 'demandwaretest1' + }, + type: 'StylaMagazineConfiguration', + UUID: '86262e5b488643c720f7c4e562' + }, + { + custom: { + basePath: 'portofolio', + enabled: true, + homepage: false, + Key_and_Sort_Order: 'Odlo Portofolio', + locales: ['en_INT'], + pipeline: 'app_storefront_base-Home-Show', + username: 'odlo-lookbook-en' + }, + type: 'StylaMagazineConfiguration', + UUID: 'cff6dd74fb9d5854b70827c039' + }, + { + custom: { + basePath: '/', + enabled: true, + homepage: true, + Key_and_Sort_Order: 'test', + locales: ['en_US', 'en_GB', 'de_DE', 'fr', 'default'], + pipeline: 'app_storefront_base-Home-Show', + username: 'odlo-lookbook-en' + }, + type: 'StylaMagazineConfiguration', + UUID: 'c57cac620b663532d5d3680067' + } + ] + + var customObjects = new Iterator(array); + return customObjects; +}; +CustomObjectMgr.prototype.customObject=null; +CustomObjectMgr.prototype.allCustomObjects=null; + +module.exports = CustomObjectMgr; \ No newline at end of file diff --git a/test/mocks/dw/svc/LocalServiceRegistry.js b/test/mocks/dw/svc/LocalServiceRegistry.js new file mode 100644 index 0000000..4d10a8f --- /dev/null +++ b/test/mocks/dw/svc/LocalServiceRegistry.js @@ -0,0 +1,34 @@ +var service = function () { + return { + createService: function () { + return new service(); + }, + getURL : function () { }, + setURL : function (path) { + return path; + }, + addParam : function (paramName, paramValue) { + return { + paramName: paramValue + } + }, + setCachingTTL : function (period) { + return period; + }, + call : function () { + return result = { + isOk: function () { + return true; + }, + object: { + errorMessage: false, + html: '

    Everything is OK
    ' + } + } + } + } +}; + +var LocalServiceRegistry = new service(); + +module.exports = LocalServiceRegistry; \ No newline at end of file diff --git a/test/mocks/dw/system/Logger.js b/test/mocks/dw/system/Logger.js new file mode 100644 index 0000000..36b29cc --- /dev/null +++ b/test/mocks/dw/system/Logger.js @@ -0,0 +1,22 @@ +var Logger = function() {}; + +Logger.warn = function(){}; +Logger.isInfoEnabled = function(){}; +Logger.error = function(){}; +Logger.debugEnabled = function(){}; +Logger.isDebugEnabled = function(){}; +Logger.warnEnabled = function(){}; +Logger.rootLogger = function(){}; +Logger.getLogger = function(){return Logger;}; +Logger.debug = function(){}; +Logger.info = function (msg) { + return msg; +}; +Logger.infoEnabled = function(){}; +Logger.isErrorEnabled = function(){}; +Logger.isWarnEnabled = function(){}; +Logger.errorEnabled = function(){}; +Logger.getRootLogger = function(){}; +Logger.fatal = function(){}; + +module.exports = Logger; \ No newline at end of file diff --git a/test/mocks/dw/system/Site.js b/test/mocks/dw/system/Site.js new file mode 100644 index 0000000..a7832fb --- /dev/null +++ b/test/mocks/dw/system/Site.js @@ -0,0 +1,52 @@ +var Site = function(){}; + +Site.prototype.getCurrencyCode = function(){}; +Site.prototype.getName = function(){}; +Site.prototype.getID = function(){}; +Site.getCurrent = function(){return new Site()}; +Site.prototype.getPreferences = function(){}; +Site.prototype.getHttpHostName = function(){}; +Site.prototype.getHttpsHostName = function(){}; +Site.prototype.getCustomPreferenceValue = function (preference) { + if (preference === 'stylaEnabled') { + return true; + } + if (preference === 'stylaCdnBaseUrl') { + return '//client-scripts.styla.com/scripts/clients/{USER}.js'; + } + if (preference === 'stylaCssBaseUrl') { + return '//client-scripts.styla.com/styles/clients/{USER}.css'; + } + if (preference === 'stylaSeoDisabled') { + return false; + } + if (preference === 'stylaSeoCachingTTL') { + return 60; + } +}; +Site.prototype.setCustomPreferenceValue = function(){}; +Site.prototype.getDefaultLocale = function(){}; +Site.prototype.getAllowedLocales = function(){}; +Site.prototype.getAllowedCurrencies = function(){}; +Site.prototype.getDefaultCurrency = function(){}; +Site.prototype.getTimezone = function(){}; +Site.prototype.getTimezoneOffset = function(){}; +Site.getCalendar = function(){return new require('../util/Calendar')();}; +Site.prototype.isOMSEnabled = function(){}; +Site.prototype.currencyCode=null; +Site.prototype.name=null; +Site.prototype.ID=null; +Site.prototype.current = null; +Site.prototype.preferences=null; +Site.prototype.httpHostName=null; +Site.prototype.httpsHostName=null; +Site.prototype.customPreferenceValue=null; +Site.prototype.defaultLocale=null; +Site.prototype.allowedLocales=null; +Site.prototype.allowedCurrencies=null; +Site.prototype.defaultCurrency=null; +Site.prototype.timezone=null; +Site.prototype.timezoneOffset=null; +Site.prototype.calendar=null; + +module.exports = Site; \ No newline at end of file diff --git a/test/mocks/dw/util/Iterator.js b/test/mocks/dw/util/Iterator.js new file mode 100644 index 0000000..577fd27 --- /dev/null +++ b/test/mocks/dw/util/Iterator.js @@ -0,0 +1,21 @@ +var Iterator = function(array) { + this.array = array; + this.index = 0; +}; + +Iterator.prototype.hasNext = function() { + return this.array && this.index < this.array.length; +}; + +Iterator.prototype.next = function() { + var result; + if (this.array && this.index < this.array.length) { + result = this.array[this.index]; + this.index = this.index + 1; + return result; + } + + throw new Error('Iterator has no more elements'); +}; + +module.exports = Iterator; diff --git a/test/unit/int_styla/scripts/init/stylaServiceInit.js b/test/unit/int_styla/scripts/init/stylaServiceInit.js new file mode 100644 index 0000000..4be9616 --- /dev/null +++ b/test/unit/int_styla/scripts/init/stylaServiceInit.js @@ -0,0 +1,35 @@ +'use strict'; + +var sinon = require('sinon'); +var assert = require('chai').assert; +var proxyquire = require('proxyquire').noCallThru().noPreserveCache(); + +var LocalServiceRegistry = require('../../../../mocks/dw/svc/LocalServiceRegistry'); + +describe('stylaServiceInit', function () { + var stylaServiceInit = proxyquire('../../../../../cartridges/int_styla_refArch/cartridge/scripts/init/stylaServiceInit', { + 'dw/svc/LocalServiceRegistry': LocalServiceRegistry + }); + + it('should create service and return service methods StylaSeoContentHttpService', function () { + var result = stylaServiceInit.StylaSeoContentHttpService; + assert.isObject(result); + assert.isFunction(result.createService); + assert.isFunction(result.getURL); + assert.isFunction(result.setURL); + assert.isFunction(result.addParam); + assert.isFunction(result.setCachingTTL); + assert.isFunction(result.call); + }); + + it('should create service and return service methods StylaVersionService', function () { + var result = stylaServiceInit.StylaVersionService; + assert.isObject(result); + assert.isFunction(result.createService); + assert.isFunction(result.getURL); + assert.isFunction(result.setURL); + assert.isFunction(result.addParam); + assert.isFunction(result.setCachingTTL); + assert.isFunction(result.call); + }); +}); diff --git a/test/unit/int_styla/scripts/stylaMain.js b/test/unit/int_styla/scripts/stylaMain.js new file mode 100644 index 0000000..d883d5c --- /dev/null +++ b/test/unit/int_styla/scripts/stylaMain.js @@ -0,0 +1,163 @@ +'use strict'; + +var sinon = require('sinon'); +var assert = require('chai').assert; +var proxyquire = require('proxyquire').noCallThru().noPreserveCache(); + +var CustomObjectMgr = require('../../../mocks/dw/object/CustomObjectMgr'); +var Logger = require('../../../mocks/dw/system/Logger'); +var Site = require('../../../mocks/dw/system/Site'); +var stylaServiceIntPath = '../../../../cartridges/int_styla_refArch/cartridge/scripts/init/stylaServiceInit'; +var LocalServiceRegistry = require('../../../mocks/dw/svc/LocalServiceRegistry'); + +describe('stylaMain', function () { + var stylaMain = proxyquire('../../../../cartridges/int_styla_refArch/cartridge/scripts/stylaMain', { + 'dw/object/CustomObjectMgr': CustomObjectMgr, + 'dw/system/Logger': Logger, + 'dw/system/Site': Site, + 'app_storefront_base/cartridge/controllers/Search': { + Show: function() { + return { success: 'success' }; + } + }, + './init/stylaServiceInit': proxyquire(stylaServiceIntPath, { + 'dw/svc/LocalServiceRegistry': LocalServiceRegistry + }) + }); + + global.response = { + setStatus: function (status) { + return status; + } + } + + global.empty = function (args) { + if (args && args.length && args !== '' && args !== null && args !== 'undefined') { + return false; + } + return true; + } + + var spy1, + spy2; + + beforeEach(function () { + spy1 = sinon.spy(Logger, 'info'); + spy2 = sinon.spy(response, 'setStatus'); + }); + + afterEach(function() { + spy1.restore(); + spy2.restore(); + }); + + it('should get render content', function () { + global.request = { + locale: 'en_US', + httpParameterMap: { + magazinepath: { + submitted: true, + stringValue: '/' + } + } + } + var result = stylaMain.GetRenderContent(); + assert.isObject(result); + assert.isTrue(result.MagazineConfiguration.valid); + assert.isFalse(result.SeoResponse.errorMessage); + assert.isString(result.SeoResponse.html); + assert.equal(result.SeoResponse.html, '
    Everything is OK
    ') + }); + + it('should get render content as null', function () { + global.request = { + locale: 'default', + httpParameterMap: { + magazinepath: { + submitted: false, + stringValue: '' + } + } + } + var result = stylaMain.GetRenderContent(); + assert.isNull(result); + }); + + it('should return alias configuration', function () { + var result = stylaMain.GetConfigForAlias('/magazine/category-1'); + assert.isObject(result); + assert.isTrue(result.valid); + assert.equal(result.path, '/magazine/category-1'); + }); + + it('should return null for alias configuration', function () { + var result = stylaMain.GetConfigForAlias(); + assert.isNull(result); + }); + + it('should log story not found, status 404', function () { + global.request = { + httpHeaders: {}, + custom: { + MagazineConfiguration: { + valid: true, + path: '/', + rootPath: '/', + username: 'odlo-lookbook-en', + pipeline: 'app_storefront_base-Home-Show', + categoryID: '', + basePath: '/', + homepage: true, + seoDisabled: false, + seoCachingTTL: 60, + jsLibUrl: '//client-scripts.styla.com/scripts/clients/odlo-lookbook-en.js', + cssUrl: '//client-scripts.styla.com/styles/clients/odlo-lookbook-en.css', + seoResponse: { + errorMessage: false, + html: '
    Everything is OK
    ', + status: 404 + } + }, + SeoResponse: { + errorMessage: false, + html: '
    Everything is OK
    ' + } + } + } + stylaMain.SetHttpStatus(); + assert.isTrue(spy1.calledWith('Styla Story not found, Status: 404')); + }); + + it('should set the status to the response object', function () { + global.request = { + httpHeaders: {}, + custom: { + MagazineConfiguration: { + valid: true, + path: '/', + rootPath: '/', + username: 'odlo-lookbook-en', + pipeline: 'app_storefront_base-Home-Show', + categoryID: '', + basePath: '/', + homepage: true, + seoDisabled: false, + seoCachingTTL: 60, + jsLibUrl: '//client-scripts.styla.com/scripts/clients/odlo-lookbook-en.js', + cssUrl: '//client-scripts.styla.com/styles/clients/odlo-lookbook-en.css', + seoResponse: { + errorMessage: false, + html: '
    Everything is OK
    ', + status: 200 + } + }, + SeoResponse: { + errorMessage: false, + html: '
    Everything is OK
    ' + } + } + } + stylaMain.SetHttpStatus(); + assert.isTrue(spy2.calledWith(200)); + }); +}); diff --git a/test/unit/int_styla_refArch/scripts/helpers/searchHelpers.js b/test/unit/int_styla_refArch/scripts/helpers/searchHelpers.js new file mode 100755 index 0000000..abfeba5 --- /dev/null +++ b/test/unit/int_styla_refArch/scripts/helpers/searchHelpers.js @@ -0,0 +1,358 @@ +var assert = require('chai').assert; +var searchHelperPath = '../../../../../cartridges/int_styla_refArch/cartridge/scripts/helpers/searchHelpers'; +var proxyquire = require('proxyquire').noCallThru().noPreserveCache(); +var sinon = require('sinon'); + +module.superModule = { + getCategoryTemplate: function () { + return 'rendering/category/categoryproducthits'; + }, + setupContentSearch: function () { }, + applyCache: function () { } +} + +module.exports = module; + +describe('search helpers', function () { + describe('setup search', function () { + var mockApiProductSearch = {}; + var mockParams1 = { srule: 'bestsellers', cgid: 'mens' }; + var mockParams2 = { srule: 'bestsellers', cgid: 'mens', preferences: { prefn1: 'pref1Value' } }; + var mockSelectedCategory = { ID: 'mens', online: true }; + + var setProductPropertiesSpy = sinon.spy(); + var addRefinementValuesSpy = sinon.spy(); + + var searchHelpersMock = proxyquire(searchHelperPath, { + 'dw/catalog/CatalogMgr': { + getSortingRule: function (srule) { + return srule; + }, + getCategory: function () { + return mockSelectedCategory; + } + }, + '*/cartridge/scripts/search/search': { + setProductProperties: setProductPropertiesSpy, + addRefinementValues: addRefinementValuesSpy + } + }); + + it('should call setProductProperties', function () { + searchHelpersMock.setupSearch(mockApiProductSearch, mockParams1); + assert.isTrue(setProductPropertiesSpy.calledWith(mockApiProductSearch, mockParams1, mockSelectedCategory, mockParams2.srule)); + assert.isTrue(addRefinementValuesSpy.notCalled); + }); + + it('should call both setProductProperties & addRefinementValues', function () { + searchHelpersMock.setupSearch(mockApiProductSearch, mockParams2); + assert.isTrue(setProductPropertiesSpy.calledWith(mockApiProductSearch, mockParams1, mockSelectedCategory, mockParams2.srule)); + assert.isTrue(addRefinementValuesSpy.calledWith(mockApiProductSearch, mockParams2.preferences)); + }); + }); + + describe('search', function () { + beforeEach(function () { + request = { custom: {} }; + }); + var productSearchStub = sinon.stub(); + var searchSpy = sinon.spy(); + var categoryMock = { + parent: { + ID: 'root' + }, + template: 'rendering/category/categoryproducthits' + }; + var productSearchModelMock = { + search: searchSpy, + getSearchRedirect: function () { + return { + getLocation: function () { + return 'some value'; + } + }; + }, + category: categoryMock + }; + var searchHelpersMock3 = proxyquire(searchHelperPath, { + 'dw/catalog/CatalogMgr': { + getSortingOptions: function () { + return; + }, + getSiteCatalog: function () { + return { getRoot: function () { return; } }; + }, + getSortingRule: function (rule) { + return rule; + }, + getCategory: function () { + return { ID: 'mens', online: true }; + } + }, + 'dw/catalog/ProductSearchModel': function () { + return productSearchModelMock; + }, + 'dw/web/URLUtils': { + url: function () { + return { + append: function () { + return 'some appened URL'; + } + }; + } + }, + '*/cartridge/scripts/helpers/pageMetaHelper': { + setPageMetaTags: function () { + return; + }, + setPageMetaData: function () { + return; + } + }, + '*/cartridge/scripts/helpers/structuredDataHelper': { + getListingPageSchema: function () { + return 'some schema'; + } + }, + '*/cartridge/models/search/productSearch': productSearchStub, + '*/cartridge/scripts/reportingUrls': { + getProductSearchReportingURLs: function () { + return ['something', 'something else']; + } + }, + '*/cartridge/scripts/search/search': { + setProductProperties: function () { + return; + }, + addRefinementValues: function () { + return; + } + } + }); + + var res = { + cachePeriod: '', + cachePeriodUnit: '', + personalized: false + }; + var mockRequest1 = { + querystring: {} + }; + var mockRequest2 = { querystring: { q: 'someValue' } }; + var mockRequest3 = { querystring: { cgid: 'someCategory', preferences: 'preferences', pmin: 'pmin', pmax: 'pmax' } }; + + afterEach(function () { + productSearchStub.reset(); + searchSpy.reset(); + }); + + it('should category search', function () { + productSearchStub.returns({ + isCategorySearch: true, + isRefinedCategorySearch: false + }); + var result = searchHelpersMock3.search(mockRequest1, res); + + assert.isTrue(searchSpy.calledOnce); + assert.equal(result.maxSlots, 4); + assert.deepEqual(result.category, { + parent: { + ID: 'root' + }, + template: 'rendering/category/categoryproducthits' + }); + assert.equal(result.categoryTemplate, 'rendering/category/categoryproducthits'); + assert.equal(result.reportingURLs.length, 2); + assert.isDefined(result.canonicalUrl); + assert.isDefined(result.schemaData); + }); + + it('should search', function () { + productSearchStub.returns({ + isCategorySearch: false, + isRefinedCategorySearch: false + }); + + categoryMock = null; + + var result = searchHelpersMock3.search(mockRequest1, res); + + assert.isTrue(searchSpy.calledOnce); + assert.equal(result.maxSlots, 4); + assert.equal(result.category, null); + assert.equal(result.categoryTemplate, null); + assert.equal(result.reportingURLs.length, 2); + }); + + it('should get a search redirect url', function () { + var result = searchHelpersMock3.search(mockRequest2); + + assert.equal(result.searchRedirect, 'some value'); + assert.isTrue(searchSpy.notCalled); + assert.equal(result.maxSlots, null); + }); + + it('should search with query string params', function () { + searchHelpersMock3.search(mockRequest3, res); + + assert.isTrue(searchSpy.calledOnce); + }); + }); + + describe('searchWithStyla', function () { + beforeEach(function () { + request = { custom: { + MagazineConfiguration: { + categoryID: 'magazine' + } + } }; + }); + var productSearchStub = sinon.stub(); + var searchSpy = sinon.spy(); + var categoryMock = { + parent: { + ID: 'root' + }, + template: 'rendering/category/categoryproducthits' + }; + var productSearchModelMock = { + search: searchSpy, + getSearchRedirect: function () { + return { + getLocation: function () { + return 'some value'; + } + }; + }, + category: categoryMock + }; + var searchHelpersMock3 = proxyquire(searchHelperPath, { + 'dw/catalog/CatalogMgr': { + getSortingOptions: function () { + return; + }, + getSiteCatalog: function () { + return { getRoot: function () { return; } }; + }, + getSortingRule: function (rule) { + return rule; + }, + getCategory: function () { + return { ID: 'mens', online: true }; + } + }, + 'dw/catalog/ProductSearchModel': function () { + return productSearchModelMock; + }, + 'dw/web/URLUtils': { + url: function () { + return { + append: function () { + return 'some appened URL'; + } + }; + } + }, + '*/cartridge/scripts/helpers/pageMetaHelper': { + setPageMetaTags: function () { + return; + }, + setPageMetaData: function () { + return; + } + }, + '*/cartridge/scripts/helpers/structuredDataHelper': { + getListingPageSchema: function () { + return 'some schema'; + } + }, + '*/cartridge/models/search/productSearch': productSearchStub, + '*/cartridge/scripts/reportingUrls': { + getProductSearchReportingURLs: function () { + return ['something', 'something else']; + } + }, + '*/cartridge/scripts/search/search': { + setProductProperties: function () { + return; + }, + addRefinementValues: function () { + return; + } + } + }); + + var res = { + cachePeriod: '', + cachePeriodUnit: '', + personalized: false + }; + var mockRequest1 = { + querystring: {} + }; + var mockRequest2 = { querystring: { q: 'someValue' } }; + var mockRequest3 = { querystring: { cgid: 'someCategory', preferences: 'preferences', pmin: 'pmin', pmax: 'pmax' } }; + + afterEach(function () { + productSearchStub.reset(); + searchSpy.reset(); + }); + + it('should category search', function () { + productSearchStub.returns({ + isCategorySearch: true, + isRefinedCategorySearch: false + }); + var result = searchHelpersMock3.search(mockRequest1, res); + + assert.isTrue(searchSpy.calledOnce); + assert.equal(result.maxSlots, 4); + assert.deepEqual(result.category, { + parent: { + ID: 'root' + }, + template: 'rendering/category/categoryproducthits' + }); + assert.equal(result.categoryTemplate, 'rendering/category/categoryproducthits'); + assert.equal(result.reportingURLs.length, 2); + assert.isDefined(result.canonicalUrl); + assert.isDefined(result.schemaData); + }); + + it('should search', function () { + productSearchStub.returns({ + isCategorySearch: false, + isRefinedCategorySearch: false + }); + + categoryMock = null; + + var result = searchHelpersMock3.search(mockRequest1, res); + + assert.isTrue(searchSpy.calledOnce); + assert.equal(result.maxSlots, 4); + assert.deepEqual(result.category, { + parent: { + ID: 'root' + }, + template: 'rendering/category/categoryproducthits' + }); + assert.equal(result.categoryTemplate, 'rendering/category/categoryproducthits'); + assert.equal(result.reportingURLs.length, 2); + }); + + it('should get a search redirect url', function () { + var result = searchHelpersMock3.search(mockRequest2); + + assert.equal(result.searchRedirect, 'some value'); + assert.isTrue(searchSpy.notCalled); + assert.equal(result.maxSlots, null); + }); + + it('should search with query string params', function () { + searchHelpersMock3.search(mockRequest3, res); + + assert.isTrue(searchSpy.calledOnce); + }); + }); +}) \ No newline at end of file diff --git a/test/unit/int_styla_refArch/scripts/init/stylaServiceInit.js b/test/unit/int_styla_refArch/scripts/init/stylaServiceInit.js new file mode 100644 index 0000000..4be9616 --- /dev/null +++ b/test/unit/int_styla_refArch/scripts/init/stylaServiceInit.js @@ -0,0 +1,35 @@ +'use strict'; + +var sinon = require('sinon'); +var assert = require('chai').assert; +var proxyquire = require('proxyquire').noCallThru().noPreserveCache(); + +var LocalServiceRegistry = require('../../../../mocks/dw/svc/LocalServiceRegistry'); + +describe('stylaServiceInit', function () { + var stylaServiceInit = proxyquire('../../../../../cartridges/int_styla_refArch/cartridge/scripts/init/stylaServiceInit', { + 'dw/svc/LocalServiceRegistry': LocalServiceRegistry + }); + + it('should create service and return service methods StylaSeoContentHttpService', function () { + var result = stylaServiceInit.StylaSeoContentHttpService; + assert.isObject(result); + assert.isFunction(result.createService); + assert.isFunction(result.getURL); + assert.isFunction(result.setURL); + assert.isFunction(result.addParam); + assert.isFunction(result.setCachingTTL); + assert.isFunction(result.call); + }); + + it('should create service and return service methods StylaVersionService', function () { + var result = stylaServiceInit.StylaVersionService; + assert.isObject(result); + assert.isFunction(result.createService); + assert.isFunction(result.getURL); + assert.isFunction(result.setURL); + assert.isFunction(result.addParam); + assert.isFunction(result.setCachingTTL); + assert.isFunction(result.call); + }); +}); diff --git a/test/unit/int_styla_refArch/scripts/middleware/cache.js b/test/unit/int_styla_refArch/scripts/middleware/cache.js new file mode 100644 index 0000000..f582c31 --- /dev/null +++ b/test/unit/int_styla_refArch/scripts/middleware/cache.js @@ -0,0 +1,49 @@ +'use strict'; + +var sinon = require('sinon'); +var assert = require('chai').assert; +var proxyquire = require('proxyquire').noCallThru().noPreserveCache(); + +var cachePath = '../../../../../cartridges/int_styla_refArch/cartridge/scripts/middleware/cache'; +var SitePath = require('../../../../mocks/dw/system/Site'); + +module.superModule = { + applyDefaultCache: function () { }, + applyPromotionSensitiveCache: function () { }, + applyInventorySensitiveCache: function () { }, + applyShortPromotionSensitiveCache: function () { } +} + +module.exports = module; + +describe('cache', function () { + var cache = proxyquire(cachePath, { + 'dw/system/Site': SitePath + }); + + var spy; + + var res = { + cachePeriod: '', + cachePeriodUnit: '', + personalized: null + } + + var req = {}; + + beforeEach(function () { + spy = sinon.spy(); + }); + + afterEach(function () { + spy.reset(); + }); + + it('should set the cache period to the Site custom preference', function () { + cache.applyStylaCustomCache(req, res, spy); + assert.equal(res.cachePeriod, 60); + assert.equal(res.cachePeriodUnit, 'minutes'); + assert.isTrue(res.personalized); + assert.isTrue(spy.calledWith()); + }); +}); \ No newline at end of file diff --git a/test/unit/int_styla_refArch/scripts/stylaMain.js b/test/unit/int_styla_refArch/scripts/stylaMain.js new file mode 100644 index 0000000..d382ce4 --- /dev/null +++ b/test/unit/int_styla_refArch/scripts/stylaMain.js @@ -0,0 +1,179 @@ +'use strict'; + +var sinon = require('sinon'); +var assert = require('chai').assert; +var proxyquire = require('proxyquire').noCallThru().noPreserveCache(); + +var CustomObjectMgr = require('../../../mocks/dw/object/CustomObjectMgr'); +var Logger = require('../../../mocks/dw/system/Logger'); +var Site = require('../../../mocks/dw/system/Site'); +var stylaServiceIntPath = '../../../../cartridges/int_styla_refArch/cartridge/scripts/init/stylaServiceInit'; +var LocalServiceRegistry = require('../../../mocks/dw/svc/LocalServiceRegistry'); + +describe('stylaMain', function () { + var stylaMain = proxyquire('../../../../cartridges/int_styla_refArch/cartridge/scripts/stylaMain', { + 'dw/object/CustomObjectMgr': CustomObjectMgr, + 'dw/system/Logger': Logger, + 'dw/system/Site': Site, + 'app_storefront_base/cartridge/controllers/Search': { + Show: function() { + return { success: 'success' }; + } + }, + './init/stylaServiceInit': proxyquire(stylaServiceIntPath, { + 'dw/svc/LocalServiceRegistry': LocalServiceRegistry + }) + }); + + global.response = { + setStatus: function (status) { + return status; + } + } + + global.empty = function (args) { + if (args && args.length && args !== '' && args !== null && args !== 'undefined') { + return false; + } + return true; + } + + var spy1, + spy2; + + beforeEach(function () { + spy1 = sinon.spy(Logger, 'info'); + spy2 = sinon.spy(response, 'setStatus'); + }); + + afterEach(function() { + spy1.restore(); + spy2.restore(); + }); + + it('should get render content', function () { + global.request = { + locale: 'en_US', + httpParameterMap: { + magazinepath: { + submitted: true, + stringValue: '/' + } + } + } + var result = stylaMain.GetRenderContent(); + assert.isObject(result); + assert.isTrue(result.MagazineConfiguration.valid); + assert.isFalse(result.SeoResponse.errorMessage); + assert.isString(result.SeoResponse.html); + assert.equal(result.SeoResponse.html, '
    Everything is OK
    ') + }); + + it('should get render content as null', function () { + global.request = { + locale: 'default', + httpParameterMap: { + magazinepath: { + submitted: false, + stringValue: '' + } + } + } + var result = stylaMain.GetRenderContent(); + assert.isNull(result); + }); + + it('should get a true response if an alias exists', function () { + global.request = { + locale: 'en_US', + custom: {} + }; + var result = stylaMain.Alias('/magazine/category-1'); + assert.isTrue(result); + assert.isObject(global.request.custom.MagazineConfiguration); + assert.isTrue(global.request.custom.MagazineConfiguration.valid); + }); + + it('should return false response if an alias does not exist', function () { + var result = stylaMain.Alias('/somethingwrong/anotherthingwrong'); + assert.isFalse(result); + }); + + it('should return alias configuration', function () { + var result = stylaMain.GetConfigForAlias('/magazine/category-1'); + assert.isObject(result); + assert.isTrue(result.valid); + assert.equal(result.path, '/magazine/category-1'); + }); + + it('should return null for alias configuration', function () { + var result = stylaMain.GetConfigForAlias(); + assert.isNull(result); + }); + + it('should log story not found, status 404', function () { + global.request = { + httpHeaders: {}, + custom: { + MagazineConfiguration: { + valid: true, + path: '/', + rootPath: '/', + username: 'odlo-lookbook-en', + pipeline: 'app_storefront_base-Home-Show', + categoryID: '', + basePath: '/', + homepage: true, + seoDisabled: false, + seoCachingTTL: 60, + jsLibUrl: '//client-scripts.styla.com/scripts/clients/odlo-lookbook-en.js', + cssUrl: '//client-scripts.styla.com/styles/clients/odlo-lookbook-en.css', + seoResponse: { + errorMessage: false, + html: '
    Everything is OK
    ', + status: 404 + } + }, + SeoResponse: { + errorMessage: false, + html: '
    Everything is OK
    ' + } + } + } + stylaMain.SetHttpStatus(); + assert.isTrue(spy1.calledWith('Styla Story not found, Status: 404')); + }); + + it('should set the status to the response object', function () { + global.request = { + httpHeaders: {}, + custom: { + MagazineConfiguration: { + valid: true, + path: '/', + rootPath: '/', + username: 'odlo-lookbook-en', + pipeline: 'app_storefront_base-Home-Show', + categoryID: '', + basePath: '/', + homepage: true, + seoDisabled: false, + seoCachingTTL: 60, + jsLibUrl: '//client-scripts.styla.com/scripts/clients/odlo-lookbook-en.js', + cssUrl: '//client-scripts.styla.com/styles/clients/odlo-lookbook-en.css', + seoResponse: { + errorMessage: false, + html: '
    Everything is OK
    ', + status: 200 + } + }, + SeoResponse: { + errorMessage: false, + html: '
    Everything is OK
    ' + } + } + } + stylaMain.SetHttpStatus(); + assert.isTrue(spy2.calledWith(200)); + }); +});