- Summary
@@ -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
Site Preferences and Custom Objects
-
- 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.
+
+ 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 @@ 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.
@@ -391,7 +388,6 @@
Magazine
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:
-
-
- Changed pipeline:
-
-
- 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:
-
-
- 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:
-
-
- 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:
-
-
- 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
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.
@@ -1094,7 +1041,6 @@
Magazine
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));
+ });
+});