Merge pull request #101 from Safelite/feature/CSR-242
Global state replacement for CMS data
This commit is contained in:
commit
28a337eec7
3 changed files with 201 additions and 46 deletions
|
|
@ -16,7 +16,7 @@ module.exports = {
|
||||||
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],
|
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],
|
||||||
coverageThreshold: {
|
coverageThreshold: {
|
||||||
global: {
|
global: {
|
||||||
statements: 70,
|
statements: 80,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -9,17 +9,84 @@ export function fetchCmsContentForPage(fmgPage) {
|
||||||
const pageDataFromCms = {};
|
const pageDataFromCms = {};
|
||||||
|
|
||||||
response.data.Result.forEach((widget) => {
|
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 we already have this widget, push it on the collection
|
||||||
if (widget.Type in pageDataFromCms) {
|
if (widgetWithReplacements.Type in pageDataFromCms) {
|
||||||
pageDataFromCms[widget.Type].push(widget.Model);
|
pageDataFromCms[widgetWithReplacements.Type].push(widgetWithReplacements.Model);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pageDataFromCms[widget.Type] = [widget.Model];
|
pageDataFromCms[widgetWithReplacements.Type] = [widgetWithReplacements.Model];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return pageDataFromCms;
|
return pageDataFromCms;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 = {
|
||||||
|
Type: widgetType,
|
||||||
|
Model: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(widgetModel).forEach(key => {
|
||||||
|
|
||||||
|
let modelWithReplacements = processWidgetItemForReplacement(widgetModel, key);
|
||||||
|
|
||||||
|
objWithReplacements.Model[key] = modelWithReplacements;
|
||||||
|
});
|
||||||
|
|
||||||
|
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') {
|
||||||
|
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];
|
||||||
}
|
}
|
||||||
|
|
@ -3,50 +3,138 @@ import { dispatch } from "@/store";
|
||||||
|
|
||||||
jest.mock("@/store", () => ({
|
jest.mock("@/store", () => ({
|
||||||
dispatch: jest.fn(),
|
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
|
describe("cms-content-helper.js", () => {
|
||||||
expect(response.PageHeaderWidget[0].HeaderText).toEqual(
|
it("Should replace strings for global state", () => {
|
||||||
"Select a year to get started"
|
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");
|
||||||
Loading…
Reference in a new issue