Merge pull request #1268 from Safelite/feature/CSR-1369

Feature/csr 1369
This commit is contained in:
chloeherdsafelite 2023-07-26 09:01:53 -04:00 committed by GitHub
commit 488ccb0dae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 502 additions and 21 deletions

View file

@ -4,7 +4,7 @@
<textBlock <textBlock
:cmsWidgetName="headerCmsWidgetName" :cmsWidgetName="headerCmsWidgetName"
typeStyle="body small bold dark" typeStyle="body small bold dark"
margin="mt-0" /> marginTopSizeOverride="0" />
<textLink <textLink
linkType="textSmall" linkType="textSmall"
text="Edit" text="Edit"
@ -12,9 +12,12 @@
@click-event="linkClicked" @click-event="linkClicked"
href="javascript:void(0)" /> href="javascript:void(0)" />
</div> </div>
<div class="small" v-for="item in content" :key="item" data-test="contentLine"> <div
{{ item }} class="small review-block-content"
</div> v-for="item in content"
:key="item"
data-test="contentLine"
v-html="item"></div>
</div> </div>
</template> </template>
@ -44,3 +47,9 @@ export default {
}, },
}; };
</script> </script>
<style lang="scss" scoped>
:deep(.review-block-content > ul) {
margin-bottom: 0rem;
}
</style>

View file

@ -1,19 +1,258 @@
import { shallowMount } from "@vue/test-utils"; import { shallowMount } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper.js"; import { getMountOptions } from "@/helpers/unit-test-helper.js";
import damageReview from "@/layouts/review/review-sections/damage-review/damage-review";
jest.mock("@/helpers/cms-content-helper", () => ({ import damageReview from "@/layouts/review/review-sections/damage-review/damage-review";
fetchCmsContentForPage: () => Promise.resolve("content"), import { damageLocationsSelected as glassConstants } from "@/constants/damage-locations-selected";
}));
const testConstants = {
cmsConstants: {
widgetNames: {
header: "DamageReviewWidget",
locations: "DamageLocationsWidget",
driverDamages: "DriverDamagesWidget",
passengerDamages: "PassengerDamagesWidget",
},
header: {
text: "Damage",
},
damageLocations: {
windshield: glassConstants.WINDSHIELD,
driver: glassConstants.DRIVER,
passenger: glassConstants.PASSENGER,
rear: glassConstants.REAR,
},
damageNames: {
vent: glassConstants.VENT,
front: glassConstants.FRONT,
back: glassConstants.BACK,
quarter: glassConstants.QUARTER,
side: glassConstants.SIDEDOOR,
},
locationCopy: {
windshield: "Windshield copy",
driver: "Driver copy",
passenger: "Passenger copy",
rear: "Rear copy",
},
damageCopy: {
vent: "Vent copy",
front: "Front copy",
back: "Back copy",
quarter: "Quarter copy",
side: "Side copy",
},
imageId: "00000000-0000-0000-0000-000000000000",
},
makeBulletedList: (items) => {
let list = "<ul>";
items.forEach((item) => {
list += `<li>${item}</li>`;
});
list += "</ul>";
return list;
},
glassItems: {
windshield: {
glassLocation: glassConstants.WINDSHIELD,
glassName: glassConstants.SINGLE,
},
rear: {
glassLocation: glassConstants.REAR,
glassName: glassConstants.STATIONARY,
},
passengerItems: {
vent: {
glassLocation: glassConstants.PASSENGER,
glassName: glassConstants.VENT,
},
front: {
glassLocation: glassConstants.PASSENGER,
glassName: glassConstants.FRONT,
},
back: {
glassLocation: glassConstants.PASSENGER,
glassName: glassConstants.BACK,
},
quarter: {
glassLocation: glassConstants.PASSENGER,
glassName: glassConstants.QUARTER,
},
side: {
glassLocation: glassConstants.PASSENGER,
glassName: glassConstants.SIDEDOOR,
},
},
driverItems: {
vent: {
glassLocation: glassConstants.DRIVER,
glassName: glassConstants.VENT,
},
front: {
glassLocation: glassConstants.DRIVER,
glassName: glassConstants.FRONT,
},
back: {
glassLocation: glassConstants.DRIVER,
glassName: glassConstants.BACK,
},
quarter: {
glassLocation: glassConstants.DRIVER,
glassName: glassConstants.QUARTER,
},
side: {
glassLocation: glassConstants.DRIVER,
glassName: glassConstants.SIDEDOOR,
},
},
},
};
let cmsContent;
describe("Damage Review Block", () => { describe("Damage Review Block", () => {
beforeEach(() => {
cmsContent = {
DamageReviewWidget: {
Text: testConstants.cmsConstants.header.text,
},
DamageLocationsWidget: {
Answers: [
{
Name: testConstants.cmsConstants.damageLocations.windshield,
Text: testConstants.cmsConstants.locationCopy.windshield,
SubText: "",
ImageId: testConstants.cmsConstants.imageId,
Image: "",
SubWidgetName: "",
},
{
Name: testConstants.cmsConstants.damageLocations.driver,
Text: testConstants.cmsConstants.locationCopy.driver,
SubText: "",
ImageId: testConstants.cmsConstants.imageId,
Image: "",
SubWidgetName: testConstants.cmsConstants.widgetNames.driverDamages,
},
{
Name: testConstants.cmsConstants.damageLocations.passenger,
Text: testConstants.cmsConstants.locationCopy.passenger,
SubText: "",
ImageId: testConstants.cmsConstants.imageId,
Image: "",
SubWidgetName: testConstants.cmsConstants.widgetNames.passengerDamages,
},
{
Name: testConstants.cmsConstants.damageLocations.rear,
Text: testConstants.cmsConstants.locationCopy.rear,
SubText: "",
ImageId: testConstants.cmsConstants.imageId,
Image: "",
SubWidgetName: "",
},
],
},
DriverDamagesWidget: {
Answers: [
{
Name: testConstants.cmsConstants.damageNames.vent,
Text: testConstants.cmsConstants.damageCopy.vent,
SubText: "",
ImageId: testConstants.cmsConstants.imageId,
Image: "",
SubWidgetName: "",
},
{
Name: testConstants.cmsConstants.damageNames.front,
Text: testConstants.cmsConstants.damageCopy.front,
SubText: "",
ImageId: testConstants.cmsConstants.imageId,
Image: "",
SubWidgetName: "",
},
{
Name: testConstants.cmsConstants.damageNames.back,
Text: testConstants.cmsConstants.damageCopy.back,
SubText: "",
ImageId: testConstants.cmsConstants.imageId,
Image: "",
SubWidgetName: "",
},
{
Name: testConstants.cmsConstants.damageNames.quarter,
Text: testConstants.cmsConstants.damageCopy.quarter,
SubText: "",
ImageId: testConstants.cmsConstants.imageId,
Image: "",
SubWidgetName: "",
},
{
Name: testConstants.cmsConstants.damageNames.side,
Text: testConstants.cmsConstants.damageCopy.side,
SubText: "",
ImageId: testConstants.cmsConstants.imageId,
Image: "",
SubWidgetName: "",
},
],
},
PassengerDamagesWidget: {
Answers: [
{
Name: testConstants.cmsConstants.damageNames.vent,
Text: testConstants.cmsConstants.damageCopy.vent,
SubText: "",
ImageId: testConstants.cmsConstants.imageId,
Image: "",
SubWidgetName: "",
},
{
Name: testConstants.cmsConstants.damageNames.front,
Text: testConstants.cmsConstants.damageCopy.front,
SubText: "",
ImageId: testConstants.cmsConstants.imageId,
Image: "",
SubWidgetName: "",
},
{
Name: testConstants.cmsConstants.damageNames.back,
Text: testConstants.cmsConstants.damageCopy.back,
SubText: "",
ImageId: testConstants.cmsConstants.imageId,
Image: "",
SubWidgetName: "",
},
{
Name: testConstants.cmsConstants.damageNames.quarter,
Text: testConstants.cmsConstants.damageCopy.quarter,
SubText: "",
ImageId: testConstants.cmsConstants.imageId,
Image: "",
SubWidgetName: "",
},
{
Name: testConstants.cmsConstants.damageNames.side,
Text: testConstants.cmsConstants.damageCopy.side,
SubText: "",
ImageId: testConstants.cmsConstants.imageId,
Image: "",
SubWidgetName: "",
},
],
},
};
});
describe("Correctly assembles damage info into a display string", () => { describe("Correctly assembles damage info into a display string", () => {
test("Basic Replace", async () => { test("Shows windshield copy when windshield damage is included", async () => {
// Arrange // Arrange
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
propsData: { propsData: {
cmsWidgetName: testConstants.cmsConstants.widgetNames.header,
damageLocationsWidgetName: testConstants.cmsConstants.widgetNames.locations,
damage: { damage: {
isRepair: false, isRepair: false,
glassToReplace: [testConstants.glassItems.windshield],
}, },
}, },
}); });
@ -22,16 +261,21 @@ describe("Damage Review Block", () => {
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
// Assert // Assert
expect(wrapper.vm.displayContent).toStrictEqual(["Windshield crack"]); expect(wrapper.vm.displayContent).toStrictEqual([
testConstants.cmsConstants.locationCopy.windshield,
]);
}); });
test("Basic Repair", async () => { test("Windshield copy is shown when order is a repair", async () => {
// Arrange // Arrange
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
propsData: { propsData: {
cmsWidgetName: testConstants.cmsConstants.widgetNames.header,
damageLocationsWidgetName: testConstants.cmsConstants.widgetNames.locations,
damage: { damage: {
isRepair: true, isRepair: true,
numberOfChips: 2, numberOfChips: 2,
glassToReplace: [],
}, },
}, },
}); });
@ -40,16 +284,21 @@ describe("Damage Review Block", () => {
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
// Assert // Assert
expect(wrapper.vm.displayContent).toStrictEqual(["Windshield repair - 2 chips"]); expect(wrapper.vm.displayContent).toStrictEqual([
testConstants.cmsConstants.locationCopy.windshield,
]);
}); });
test("Basic Repair - no s with 1 chip", async () => { test("Rear windshield copy shows when rear damage is present", async () => {
// Arrange // Arrange
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
propsData: { propsData: {
cmsWidgetName: testConstants.cmsConstants.widgetNames.header,
damageLocationsWidgetName: testConstants.cmsConstants.widgetNames.locations,
damage: { damage: {
isRepair: true, isRepair: false,
numberOfChips: 1, numberOfChips: null,
glassToReplace: [testConstants.glassItems.rear],
}, },
}, },
}); });
@ -58,7 +307,116 @@ describe("Damage Review Block", () => {
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
// Assert // Assert
expect(wrapper.vm.displayContent).toStrictEqual(["Windshield repair - 1 chip"]); expect(wrapper.vm.displayContent).toStrictEqual([
testConstants.cmsConstants.locationCopy.rear,
]);
});
test("Driver side copy and items are shown when driver side damage is present", async () => {
// Arrange
const { wrapper } = setupMocks({
propsData: {
cmsWidgetName: testConstants.cmsConstants.widgetNames.header,
damageLocationsWidgetName: testConstants.cmsConstants.widgetNames.locations,
damage: {
isRepair: false,
numberOfChips: null,
glassToReplace: [
testConstants.glassItems.driverItems.back,
testConstants.glassItems.driverItems.front,
],
},
},
});
// Act
await wrapper.vm.$nextTick();
// Assert
expect(wrapper.vm.displayContent).toStrictEqual([
testConstants.cmsConstants.locationCopy.driver,
testConstants.makeBulletedList([
testConstants.cmsConstants.damageCopy.front,
testConstants.cmsConstants.damageCopy.back,
]),
]);
});
test("Passenger side copy and items are shown when passenger side damage is present", async () => {
// Arrange
const { wrapper } = setupMocks({
propsData: {
cmsWidgetName: testConstants.cmsConstants.widgetNames.header,
damageLocationsWidgetName: testConstants.cmsConstants.widgetNames.locations,
damage: {
isRepair: false,
numberOfChips: null,
glassToReplace: [
testConstants.glassItems.passengerItems.quarter,
testConstants.glassItems.passengerItems.vent,
testConstants.glassItems.passengerItems.side,
],
},
},
});
// Act
await wrapper.vm.$nextTick();
// Assert
expect(wrapper.vm.displayContent).toStrictEqual([
testConstants.cmsConstants.locationCopy.passenger,
testConstants.makeBulletedList([
testConstants.cmsConstants.damageCopy.vent,
testConstants.cmsConstants.damageCopy.quarter,
testConstants.cmsConstants.damageCopy.side,
]),
]);
});
test("All relevant sections are shown in order in multiglass scenario", async () => {
// Arrange
const { wrapper } = setupMocks({
propsData: {
cmsWidgetName: testConstants.cmsConstants.widgetNames.header,
damageLocationsWidgetName: testConstants.cmsConstants.widgetNames.locations,
damage: {
isRepair: false,
numberOfChips: null,
glassToReplace: [
testConstants.glassItems.windshield,
testConstants.glassItems.rear,
testConstants.glassItems.driverItems.vent,
testConstants.glassItems.driverItems.front,
testConstants.glassItems.driverItems.back,
testConstants.glassItems.passengerItems.quarter,
testConstants.glassItems.passengerItems.back,
testConstants.glassItems.passengerItems.side,
],
},
},
});
// Act
await wrapper.vm.$nextTick();
// Assert
expect(wrapper.vm.displayContent).toStrictEqual([
testConstants.cmsConstants.locationCopy.windshield,
testConstants.cmsConstants.locationCopy.driver,
testConstants.makeBulletedList([
testConstants.cmsConstants.damageCopy.vent,
testConstants.cmsConstants.damageCopy.front,
testConstants.cmsConstants.damageCopy.back,
]),
testConstants.cmsConstants.locationCopy.passenger,
testConstants.makeBulletedList([
testConstants.cmsConstants.damageCopy.back,
testConstants.cmsConstants.damageCopy.quarter,
testConstants.cmsConstants.damageCopy.side,
]),
testConstants.cmsConstants.locationCopy.rear,
]);
}); });
}); });
}); });
@ -66,6 +424,16 @@ describe("Damage Review Block", () => {
function setupMocks(customMountOptions) { function setupMocks(customMountOptions) {
const mountOptions = getMountOptions(customMountOptions); const mountOptions = getMountOptions(customMountOptions);
const mockMixin = {
methods: {
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
return cmsContent?.[widgetName]?.[cmsFieldName] ?? "";
}),
},
};
mountOptions.global.mixins = [mockMixin];
const wrapper = shallowMount(damageReview, mountOptions); const wrapper = shallowMount(damageReview, mountOptions);
wrapper.vm.setCmsContent = jest.fn(); wrapper.vm.setCmsContent = jest.fn();
return { wrapper }; return { wrapper };

View file

@ -7,11 +7,13 @@
<script> <script>
import reviewBlock from "@/layouts/review/review-block/review-block"; import reviewBlock from "@/layouts/review/review-block/review-block";
import { damageLocationsSelected } from "@/constants/damage-locations-selected.js";
export default { export default {
name: "damage-review", name: "damage-review",
props: { props: {
cmsWidgetName: String, cmsWidgetName: String,
damageLocationsWidgetName: String,
damage: Object, damage: Object,
}, },
data() { data() {
@ -21,17 +23,115 @@ export default {
editClicked() { editClicked() {
this.$emit("edit-clicked"); this.$emit("edit-clicked");
}, },
getAnswersNullSafe(widgetName) {
const rawAnswers = this.getCmsContent(widgetName, "Answers");
return rawAnswers ? rawAnswers : [];
},
getLocationAnswer(damageLocation) {
return this.locationAnswers?.find((answer) => answer.Name === damageLocation);
},
getGlassPieces(damageLocation) {
return this.damage?.glassToReplace?.filter(
(item) => item.glassLocation === damageLocation
);
},
generateBulletedListFromAnswers(answers) {
let list = "<ul>";
answers.forEach((answer) => {
list += `<li>${answer.Text}</li>`;
});
list += "</ul>";
return list;
},
}, },
computed: { computed: {
displayContent() { displayContent() {
return [ return [
this.damage.isRepair ...this.windshieldCopy,
? `Windshield repair - ${this.damage.numberOfChips} chip${ ...this.driverSideCopy,
this.damage.numberOfChips !== 1 ? "s" : "" ...this.passengerSideCopy,
}` ...this.rearCopy,
: "Windshield crack",
]; ];
}, },
hasWindshieldDamage() {
const windshieldPieces = this.getGlassPieces(damageLocationsSelected.WINDSHIELD);
return this.damage.isRepair || !!windshieldPieces?.length;
},
hasDriverSideDamage() {
const driverPieces = this.getGlassPieces(damageLocationsSelected.DRIVER);
return !!driverPieces?.length;
},
hasPassengerSideDamage() {
const passengerPieces = this.getGlassPieces(damageLocationsSelected.PASSENGER);
return !!passengerPieces?.length;
},
hasRearDamage() {
const rearPieces = this.getGlassPieces(damageLocationsSelected.REAR);
return !!rearPieces?.length;
},
windshieldCopy() {
const answerContent = this.getLocationAnswer(damageLocationsSelected.WINDSHIELD);
if (this.hasWindshieldDamage) {
return [answerContent?.Text];
} else {
return [];
}
},
driverSideCopy() {
const answerContent = this.getLocationAnswer(damageLocationsSelected.DRIVER);
const damageAnswers = this.getAnswersNullSafe(answerContent?.SubWidgetName);
const driverSideItems = this.getGlassPieces(damageLocationsSelected.DRIVER);
const damageAnswersOnOrder = damageAnswers?.filter((answer) =>
driverSideItems?.some((glassPiece) => answer.Name === glassPiece.glassName)
);
if (this.hasDriverSideDamage) {
return [
answerContent?.Text,
this.generateBulletedListFromAnswers(damageAnswersOnOrder),
];
} else {
return [];
}
},
passengerSideCopy() {
const answerContent = this.getLocationAnswer(damageLocationsSelected.PASSENGER);
const damageAnswers = this.getAnswersNullSafe(answerContent?.SubWidgetName);
const passengerSideItems = this.getGlassPieces(damageLocationsSelected.PASSENGER);
const damageAnswersOnOrder = damageAnswers?.filter((answer) =>
passengerSideItems?.some((glassPiece) => answer.Name === glassPiece.glassName)
);
if (this.hasPassengerSideDamage) {
return [
answerContent?.Text,
this.generateBulletedListFromAnswers(damageAnswersOnOrder),
];
} else {
return [];
}
},
rearCopy() {
const answerContent = this.getLocationAnswer(damageLocationsSelected.REAR);
if (this.hasRearDamage) {
return [answerContent?.Text];
} else {
return [];
}
},
locationAnswers() {
return this.getAnswersNullSafe(this.damageLocationsWidgetName);
},
}, },
components: { components: {
reviewBlock, reviewBlock,

View file

@ -48,6 +48,7 @@
<damageReview <damageReview
cmsWidgetName="DamageReviewWidget" cmsWidgetName="DamageReviewWidget"
damageLocationsWidgetName="DamageLocationsWidget"
:damage="damageInfo" :damage="damageInfo"
@edit-clicked="editDamage" /> @edit-clicked="editDamage" />

View file

@ -528,6 +528,9 @@ export const getters = {
}, },
eventBus: (state) => state.applicationUser.eventBus, eventBus: (state) => state.applicationUser.eventBus,
damage: (state) => state.order.damage, damage: (state) => state.order.damage,
hasExactlyOneChip: (state) => {
return state.order.damage?.numberOfChips === 1;
},
hasAnyNonWindshieldGlassParts: (state) => { hasAnyNonWindshieldGlassParts: (state) => {
const nonWindshieldItems = state.order.damage.glassToReplace?.filter( const nonWindshieldItems = state.order.damage.glassToReplace?.filter(
(glassToReplace) => glassToReplace.glassLocation != "Windshield" (glassToReplace) => glassToReplace.glassLocation != "Windshield"