From 3cbc071bb48d8e4d94a56f93294f54dafe255e14 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 16 Dec 2021 14:09:26 -0500 Subject: [PATCH 1/3] Created mapper methods --- src/helpers/cms-content-helper.js | 70 +++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/src/helpers/cms-content-helper.js b/src/helpers/cms-content-helper.js index e0a12d8fe..0c80b5bd3 100644 --- a/src/helpers/cms-content-helper.js +++ b/src/helpers/cms-content-helper.js @@ -9,17 +9,79 @@ export function fetchCmsContentForPage(fmgPage) { const pageDataFromCms = {}; response.data.Result.forEach((widget) => { - if (Object.values(widgetNames).includes(widget.Type)) { + + let widgetWithReplacements = findAndReplaceGlobalStateValues(widget.Model, widget.Type); + + if (Object.values(widgetNames).includes(widgetWithReplacements.Type)) { // If we already have this widget, push it on the collection - if (widget.Type in pageDataFromCms) { - pageDataFromCms[widget.Type].push(widget.Model); + if (widgetWithReplacements.Type in pageDataFromCms) { + pageDataFromCms[widgetWithReplacements.Type].push(widgetWithReplacements.Model); return; } - pageDataFromCms[widget.Type] = [widget.Model]; + pageDataFromCms[widgetWithReplacements.Type] = [widgetWithReplacements.Model]; } }); return pageDataFromCms; }); } + +function mapStringToState(str) { + + // Pull the state string out of our dynamic string from the CMS + const regexExp = new RegExp('{(.*?):(.*?)}', 'g'); + const matches = [...str.matchAll(regexExp)]; + const stateString = matches.map(m => m[2]).toString(); + + // Convert the state string to a state object + let storeState = store.state; + + for (const s of stateString.split('.')) { + if (storeState[s] != undefined) { + storeState = storeState[s]; + } else { + return false; // If we can't find our state variable, break our loop. Don't break our code. + } + } + + return storeState; +} + +function findAndReplaceGlobalStateValues(widgetModel, widgetType) { + + const objWithReplacements = { + Type: widgetType, + Model: {} + }; + + Object.keys(widgetModel).forEach(key => { + + let modelWithReplacements = processWidgetItemForReplacement(widgetModel, key); + + objWithReplacements.Model[key] = modelWithReplacements; + }); + + return objWithReplacements; +} + +function processWidgetItemForReplacement(widgetModel, key) { + // If we have a string, and it needs to be replaced. + if (typeof widgetModel[key] === 'string') { + if (widgetModel[key].includes('{globalState:')) { + widgetModel[key] = mapStringToState(widgetModel[key]); + } + return widgetModel[key]; + } + + // If we have an object. array, etc + if (typeof widgetModel[key] === 'object' && Object.keys(widgetModel[key]).length) { + Object.keys(widgetModel[key]).forEach(item => { + processWidgetItemForReplacement(widgetModel[key], item); + }); + + return widgetModel[key]; + } + + return widgetModel[key]; +} \ No newline at end of file From 01fa5ac057db984e3c2d229e1534a9816b2cac08 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 16 Dec 2021 14:49:40 -0500 Subject: [PATCH 2/3] Some comments --- src/helpers/cms-content-helper.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/helpers/cms-content-helper.js b/src/helpers/cms-content-helper.js index 0c80b5bd3..1d901d27e 100644 --- a/src/helpers/cms-content-helper.js +++ b/src/helpers/cms-content-helper.js @@ -27,6 +27,7 @@ export function fetchCmsContentForPage(fmgPage) { }); } +// Function to convert a string, into a matching global state item. function mapStringToState(str) { // Pull the state string out of our dynamic string from the CMS @@ -48,6 +49,8 @@ function mapStringToState(str) { return storeState; } +// Parent function for processWidgetItemForReplacement. This will loop through the parent +// object and pass any objects that need additional processing to the processWidgetItemForReplacement function. function findAndReplaceGlobalStateValues(widgetModel, widgetType) { const objWithReplacements = { @@ -65,6 +68,8 @@ function findAndReplaceGlobalStateValues(widgetModel, widgetType) { return objWithReplacements; } +// This function will process the widget item and replace any global state variables with their values. +// This is a recursive function, it will call itself until it runs out of items to iterate on given the object. function processWidgetItemForReplacement(widgetModel, key) { // If we have a string, and it needs to be replaced. if (typeof widgetModel[key] === 'string') { From 8958dfe0bb6070df6a44e0f7fd6f2b0f86304bd3 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 17 Dec 2021 13:11:59 -0500 Subject: [PATCH 3/3] Unit tests updates --- jest.config.js | 2 +- src/helpers/cms-helper.spec.js | 170 +++++++++++++++++++++++++-------- 2 files changed, 130 insertions(+), 42 deletions(-) diff --git a/jest.config.js b/jest.config.js index e36f1a70d..fd7713966 100644 --- a/jest.config.js +++ b/jest.config.js @@ -16,7 +16,7 @@ module.exports = { testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"], coverageThreshold: { global: { - statements: 70, + statements: 80, }, }, }; diff --git a/src/helpers/cms-helper.spec.js b/src/helpers/cms-helper.spec.js index e9bd66653..0f03ab604 100644 --- a/src/helpers/cms-helper.spec.js +++ b/src/helpers/cms-helper.spec.js @@ -3,50 +3,138 @@ import { dispatch } from "@/store"; jest.mock("@/store", () => ({ dispatch: jest.fn(), + state: { + order: { vehicle: { year: "2019", make: "Acura" } } + } })); -it("cms-content-helper: Should return data from CMS", () => { - // Arrange - const cmsMockData = { - Result: [ - { - Type: "VehicleBannerWidget", - Model: { - ImageId: "28452dcb-7762-4cc9-ab09-7643d0b89203", - GenericVehicleImage: - "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3", - GenericVehicleImageFilePath: - "images/default-source/default-album/blurred-image.jpg", - }, - }, - { - Type: "PageHeaderWidget", - Model: { - HeaderText: "Select a year to get started", - }, - }, - { - Type: "PageHeaderWidget", - Model: { - HeaderText: "Select a model", - }, - }, - { - Type: "RadioQuestionWidget", - Model: { - QuestionText: "What year is your vehicle?", - }, - }, - ], - }; - dispatch.mockImplementation(() => Promise.resolve({ data: cmsMockData })); +describe("cms-content-helper.js", () => { + it("Should return data from CMS", () => { + // Arrange + const cmsMockData = { + Result: [ + { + Type: "PageHeaderWidget", + Model: { + HeaderText: "Select a year to get started", + }, + }, + { + Type: "PageHeaderWidget", + Model: { + HeaderText: "Select a model", + }, + }, + { + Type: "RadioQuestionWidget", + Model: { + QuestionText: "What year is your vehicle?", + }, + }, + ], + }; + + dispatch.mockImplementation(() => Promise.resolve({ data: cmsMockData })); + + // Act + fetchCmsContentForPage("testPage").then((response) => { + // Assert + expect(response.PageHeaderWidget[0].HeaderText).toEqual( + "Select a year to get started" + ); + }); + }); - // Act - fetchCmsContentForPage("testPage").then((response) => { - // Assert - expect(response.PageHeaderWidget[0].HeaderText).toEqual( - "Select a year to get started" - ); +}); + +describe("cms-content-helper.js", () => { + it("Should replace strings for global state", () => { + const cmsMockData = { + Result: [ + { + Type: "PageHeaderWidget", + Model: { + HeaderText: "{globalState:order.vehicle.year}", + }, + }, + ], + }; + + dispatch.mockImplementation(() => Promise.resolve({ data: cmsMockData })); + + fetchCmsContentForPage("testPage").then((response) => { + // Assert + expect(response.PageHeaderWidget[0].HeaderText).toEqual( + "2019" + ); + }); }); }); + +describe("cms-content-helper.js", () => { + it("Should replace strings for global state, and leave others the same", () => { + + const cmsMockData = { + Result: [ + { + Type: "PageHeaderWidget", + Model: { + HeaderText: "{globalState:order.vehicle.year}", + }, + }, + { + Type: "RadioQuestionWidget", + Model: { + ExampleText: "My widget value!", + }, + }, + ], + }; + + dispatch.mockImplementation(() => Promise.resolve({ data: cmsMockData })); + + fetchCmsContentForPage("testPage").then((response) => { + // Assert + expect(response.PageHeaderWidget[0].HeaderText).toEqual("2019"); + expect(response.RadioQuestionWidget[0].ExampleText).toEqual("My widget value!"); + + }); + }); +}); + +describe("cms-content-helper.js", () => { + it("Should replace strings for global state in nested objects", () => { + + const cmsMockData = { + Result: [ + { + Type: "PageHeaderWidget", + Model: { + HeaderText: "{globalState:order.vehicle.year}", + }, + }, + { + Type: "RadioQuestionWidget", + Model: { + OtherObjectInside: { + ExampleText: "{globalState:order.vehicle.make}", + } + }, + }, + ], + }; + + dispatch.mockImplementation(() => Promise.resolve({ data: cmsMockData })); + + fetchCmsContentForPage("testPage").then((response) => { + // Assert + + expect(response.PageHeaderWidget[0].HeaderText).toEqual("2019"); + expect(response.RadioQuestionWidget[0].OtherObjectInside.ExampleText).toEqual("Acura"); + + }); + }); +}); + +test.todo("String cannot be mapped to global state"); \ No newline at end of file