Merge pull request #363 from Safelite/feature/digital/SSR-494
Feature/digital/ssr 494
This commit is contained in:
commit
8c78204602
9 changed files with 1495 additions and 1399 deletions
|
|
@ -1,12 +1,12 @@
|
|||
// Components
|
||||
import textareaQuestion from '@/digital-components/textarea-question/textarea-question.vue';
|
||||
import textareaQuestion from '@/digital-components/textarea-question/textarea-question';
|
||||
|
||||
// Supporting Files
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { getRandomString, getRandomInt } from '@/helpers/data-generation';
|
||||
import { getRandomString, getRandomInt } from '@/helpers/data-generation.js';
|
||||
import { nextTick } from 'vue';
|
||||
import { defineRule } from 'vee-validate';
|
||||
import { required } from '@/helpers/validation-rules';
|
||||
import { required } from '@/helpers/validation-rules.js';
|
||||
|
||||
describe('textarea-question.vue', () => {
|
||||
const mockMixin = {
|
||||
|
|
@ -69,7 +69,7 @@ describe('textarea-question.vue', () => {
|
|||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
const expected = `0/${maxLength}`;
|
||||
const expected = `${maxLength}/${maxLength}`;
|
||||
|
||||
// Act
|
||||
// Assert
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
</textarea>
|
||||
</div>
|
||||
<p class="character-count margin-top-8">
|
||||
{{ characterCount }}/{{ maxLength }} characters remaining
|
||||
{{ maxLength - characterCount }}/{{ maxLength }} characters remaining
|
||||
</p>
|
||||
<div
|
||||
v-show="errorMessage"
|
||||
|
|
@ -136,7 +136,7 @@ export default {
|
|||
const data = evt.type === 'paste' ? (evt.clipboardData || window.clipboardData) : evt.dataTransfer;
|
||||
const value = data.getData('Text')?.trim();
|
||||
|
||||
this.$emit('update:modelValue', value);
|
||||
this.$emit('update:modelValue', this.modelValue + value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
// Components
|
||||
import contactDetails from '@/layouts/contact-details/contact-details.vue';
|
||||
import contactDetails from '@/layouts/contact-details/contact-details';
|
||||
|
||||
// Supporting Files
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper';
|
||||
import { getRandomString, getRandomInt } from '@/helpers/data-generation';
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
import { getRandomString, getRandomInt, getRandomBoolean } from '@/helpers/data-generation.js';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { navigationScenarios } from '@/router/router-constants/navigation-scenarios';
|
||||
import { navigationScenarios } from '@/router/router-constants/navigation-scenarios.js';
|
||||
import { useMainStore } from '@/store/index.js';
|
||||
|
||||
describe('contactDetails.vue', () => {
|
||||
describe('Rendering', () => {
|
||||
|
|
@ -153,7 +154,7 @@ describe('contactDetails.vue', () => {
|
|||
// Assert
|
||||
expect(footer.exists()).toBe(true);
|
||||
});
|
||||
test('Mocked store yields expected data', () => {
|
||||
test('Mocked store with no contact info yields expected data', () => {
|
||||
// Arrange
|
||||
const mountOptions = getMountOptions();
|
||||
|
||||
|
|
@ -184,9 +185,50 @@ describe('contactDetails.vue', () => {
|
|||
// Assert
|
||||
expect(wrapper.vm.firstName).toBe(firstName);
|
||||
expect(wrapper.vm.lastName).toBe(lastName);
|
||||
expect(wrapper.vm.email).toBe(emailAddress);
|
||||
expect(wrapper.vm.emailAddress).toBe(emailAddress);
|
||||
expect(wrapper.vm.phoneNumber).toBe(phoneNumber);
|
||||
});
|
||||
test('Mock store with contact info yields expected data', () => {
|
||||
// Arrange
|
||||
const customer = {
|
||||
firstName: getRandomString(4, 15),
|
||||
lastName: getRandomString(4, 15),
|
||||
emailAddress: getRandomString(10, 20),
|
||||
phoneNumber: getRandomInt(1000000000, 9999999999)
|
||||
};
|
||||
const contactInfo = {
|
||||
firstName: getRandomString(4, 15),
|
||||
lastName: getRandomString(4, 15),
|
||||
emailAddress: getRandomString(10, 20),
|
||||
phoneNumber: getRandomInt(1000000000, 9999999999),
|
||||
requestTextUpdates: getRandomBoolean(),
|
||||
notesForTechnician: getRandomString(50, 100)
|
||||
};
|
||||
const mainInitialState = {
|
||||
order: {
|
||||
customer,
|
||||
contactInfo
|
||||
}
|
||||
};
|
||||
const mountOptions = getMountOptions();
|
||||
mountOptions.global = {
|
||||
plugins: [createTestingPinia({
|
||||
initialState: {
|
||||
main: mainInitialState
|
||||
}
|
||||
})]
|
||||
};
|
||||
|
||||
const wrapper = shallowMount(contactDetails, mountOptions);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.firstName).toBe(contactInfo.firstName);
|
||||
expect(wrapper.vm.lastName).toBe(contactInfo.lastName);
|
||||
expect(wrapper.vm.emailAddress).toBe(contactInfo.emailAddress);
|
||||
expect(wrapper.vm.phoneNumber).toBe(contactInfo.phoneNumber);
|
||||
expect(wrapper.vm.requestTextUpdates).toBe(contactInfo.requestTextUpdates);
|
||||
expect(wrapper.vm.notesForTechnician).toBe(contactInfo.notesForTechnician);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Navigation', () => {
|
||||
|
|
@ -222,5 +264,44 @@ describe('contactDetails.vue', () => {
|
|||
expect(wrapper.vm.$router.navigate)
|
||||
.toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD, undefined);
|
||||
});
|
||||
test('Forward button click updates contact info', () => {
|
||||
// Arrange
|
||||
const mountOptions = getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
},
|
||||
global: {
|
||||
plugins: [createTestingPinia()]
|
||||
}
|
||||
});
|
||||
const wrapper = shallowMount(contactDetails, mountOptions);
|
||||
const firstName = getRandomString(4, 15);
|
||||
const lastName = getRandomString(4, 15);
|
||||
const emailAddress = getRandomString(10, 20);
|
||||
const phoneNumber = getRandomInt(1000000000, 9999999999);
|
||||
const requestTextUpdates = getRandomBoolean();
|
||||
const notesForTechnician = getRandomString(1, 100);
|
||||
wrapper.setData({
|
||||
firstName,
|
||||
lastName,
|
||||
emailAddress,
|
||||
phoneNumber,
|
||||
requestTextUpdates,
|
||||
notesForTechnician
|
||||
});
|
||||
|
||||
// Act
|
||||
wrapper.vm.forwardButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(useMainStore().updateContactInfo).toHaveBeenCalledWith({
|
||||
firstName,
|
||||
lastName,
|
||||
emailAddress,
|
||||
phoneNumber,
|
||||
requestTextUpdates,
|
||||
notesForTechnician
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@
|
|||
:validationRules="rules.lastName" />
|
||||
<textboxQuestion
|
||||
ref="emailQuestion"
|
||||
v-model="email"
|
||||
v-model="emailAddress"
|
||||
class="margin-top-16"
|
||||
inputId="email"
|
||||
inputId="emailAddress"
|
||||
:cmsWidgetName="widget.emailQuestion"
|
||||
isRequired
|
||||
:validationRules="rules.email" />
|
||||
:validationRules="rules.emailAddress" />
|
||||
<textboxQuestion
|
||||
ref="phoneNumberQuestion"
|
||||
v-model="phoneNumber"
|
||||
|
|
@ -50,6 +50,7 @@
|
|||
<checkbox
|
||||
ref="requestTextUpdatesCheckbox"
|
||||
v-model="requestTextUpdates"
|
||||
:isChecked="requestTextUpdates"
|
||||
class="margin-top-8"
|
||||
checkboxName="requestTextUpdates"
|
||||
buttonID="requestTextUpdates"
|
||||
|
|
@ -63,8 +64,8 @@
|
|||
:isDisabled="false"
|
||||
:isRequired="false"
|
||||
:cmsWidgetName="widget.notesQuestion"
|
||||
maxLength="500"
|
||||
:inputRows="3" />
|
||||
maxLength="250"
|
||||
:inputRows="4" />
|
||||
|
||||
<p
|
||||
ref="disclaimerText"
|
||||
|
|
@ -89,7 +90,7 @@
|
|||
ref="siteFooter"
|
||||
:cmsWidgetName="widget.siteFooter"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
@forwardClicked="navigateForward"
|
||||
@forwardClicked="forwardButtonAction"
|
||||
@backClicked="backButtonAction" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -98,20 +99,20 @@
|
|||
</template>
|
||||
<script>
|
||||
// Components
|
||||
import siteHeader from '@/iss-components/site-header/site-header.vue';
|
||||
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
|
||||
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue';
|
||||
import textboxQuestion from '@/digital-components/textbox-question/textbox-question.vue';
|
||||
import checkbox from '@/ux-components/checkbox/checkbox.vue';
|
||||
import textareaQuestion from '@/digital-components/textarea-question/textarea-question.vue';
|
||||
import textLink from '@/ux-components/text-link/text-link.vue';
|
||||
import siteHeader from '@/iss-components/site-header/site-header';
|
||||
import siteFooter from '@/iss-components/site-footer/site-footer';
|
||||
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header';
|
||||
import textboxQuestion from '@/digital-components/textbox-question/textbox-question';
|
||||
import checkbox from '@/ux-components/checkbox/checkbox';
|
||||
import textareaQuestion from '@/digital-components/textarea-question/textarea-question';
|
||||
import textLink from '@/ux-components/text-link/text-link';
|
||||
|
||||
// Supporting files
|
||||
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
|
||||
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper.js';
|
||||
import { Form } from 'vee-validate';
|
||||
import BaseFormMixin from '@/mixins/base-form-mixin';
|
||||
import { useMainStore } from '@/store';
|
||||
import globalRules from '@/constants/global-rules';
|
||||
import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
||||
import { useMainStore } from '@/store/index.js';
|
||||
import globalRules from '@/constants/global-rules.js';
|
||||
|
||||
export default {
|
||||
name: 'contact-details',
|
||||
|
|
@ -135,13 +136,19 @@ export default {
|
|||
});
|
||||
},
|
||||
data() {
|
||||
const { firstName,
|
||||
lastName,
|
||||
emailAddress,
|
||||
phoneNumber,
|
||||
requestTextUpdates,
|
||||
notesForTechnician } = useMainStore().contactInfo;
|
||||
return {
|
||||
firstName: useMainStore().order.customer.firstName,
|
||||
lastName: useMainStore().order.customer.lastName,
|
||||
email: useMainStore().order.customer.emailAddress,
|
||||
phoneNumber: useMainStore().order.customer.phoneNumber,
|
||||
requestTextUpdates: false,
|
||||
notesForTechnician: '',
|
||||
firstName,
|
||||
lastName,
|
||||
emailAddress,
|
||||
phoneNumber,
|
||||
requestTextUpdates,
|
||||
notesForTechnician,
|
||||
widget: {
|
||||
siteHeader: 'SiteHeaderWidget',
|
||||
siteSubHeader: 'SiteSubHeaderWidget',
|
||||
|
|
@ -157,7 +164,7 @@ export default {
|
|||
rules: {
|
||||
firstName: globalRules.FIRST_NAME_REQUIRED,
|
||||
lastName: globalRules.LAST_NAME_REQUIRED,
|
||||
email: `${globalRules.EMAIL_ADDRESS_REQUIRED}|${globalRules.EMAIL_ADDRESS_FORMAT}`,
|
||||
emailAddress: `${globalRules.EMAIL_ADDRESS_REQUIRED}|${globalRules.EMAIL_ADDRESS_FORMAT}`,
|
||||
phoneNumber: `${globalRules.PHONE_NUMBER_REQUIRED}|${globalRules.PHONE_NUMBER_FORMAT}`
|
||||
}
|
||||
};
|
||||
|
|
@ -188,6 +195,15 @@ export default {
|
|||
* @summary Steps to perform when forward button clicked.
|
||||
*/
|
||||
forwardButtonAction() {
|
||||
const contactInfo = {
|
||||
firstName: this.firstName,
|
||||
lastName: this.lastName,
|
||||
emailAddress: this.emailAddress,
|
||||
phoneNumber: this.phoneNumber,
|
||||
requestTextUpdates: this.requestTextUpdates,
|
||||
notesForTechnician: this.notesForTechnician
|
||||
};
|
||||
useMainStore().updateContactInfo(contactInfo);
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD,
|
||||
this.$route);
|
||||
}
|
||||
|
|
|
|||
2651
src/store/index.js
2651
src/store/index.js
File diff suppressed because it is too large
Load diff
|
|
@ -2,7 +2,7 @@ import { useMainStore } from '@/store';
|
|||
import { createApp } from 'vue';
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import globalMethods from "@/global-methods";
|
||||
import App from '@/App.vue';
|
||||
import App from '@/App';
|
||||
import { getRandomString, getRandomGuid, getRandomInt, getRandomBoolean } from '@/helpers/data-generation';
|
||||
import { coverageStatuses } from "@/constants/coverage-statuses.js";
|
||||
|
||||
|
|
@ -422,5 +422,45 @@ describe("Store", () => {
|
|||
expect(store.payment.insuranceCoverage.isVerified).toBe(false);
|
||||
expect(store.payment.insuranceCoverage.coverageStatus).toBe(coverageStatuses.PENDING);
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateContactInfo method', () => {
|
||||
it('UpdateContactInfo updates contact info in store', () => {
|
||||
// Arrange
|
||||
const firstName = getRandomString(4, 10);
|
||||
const lastName = getRandomString(5, 15);
|
||||
const emailAddress = false;
|
||||
const phoneNumber = getRandomInt(1000000000, 9999999999);
|
||||
const requestTextUpdates = getRandomBoolean();
|
||||
const notesForTechnician = getRandomString(50, 150);
|
||||
|
||||
// Act
|
||||
store.updateContactInfo({ firstName,
|
||||
lastName,
|
||||
emailAddress,
|
||||
phoneNumber,
|
||||
requestTextUpdates,
|
||||
notesForTechnician });
|
||||
|
||||
// Assert
|
||||
expect(store.contactInfo.firstName).toEqual(firstName);
|
||||
expect(store.contactInfo.lastName).toEqual(lastName);
|
||||
expect(store.contactInfo.emailAddress).toEqual(emailAddress);
|
||||
expect(store.contactInfo.phoneNumber).toEqual(phoneNumber);
|
||||
expect(store.contactInfo.requestTextUpdates).toEqual(requestTextUpdates);
|
||||
expect(store.contactInfo.notesForTechnician).toEqual(notesForTechnician);
|
||||
});
|
||||
it('All null values => contact info set in store to all nulls', () => {
|
||||
// Act
|
||||
store.updateContactInfo({});
|
||||
|
||||
// Assert
|
||||
expect(store.contactInfo.firstName).toEqual('');
|
||||
expect(store.contactInfo.lastName).toEqual('');
|
||||
expect(store.contactInfo.emailAddress).toEqual('');
|
||||
expect(store.contactInfo.phoneNumber).toEqual('');
|
||||
expect(store.contactInfo.requestTextUpdates).toEqual(false);
|
||||
expect(store.contactInfo.notesForTechnician).toEqual('');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -75,4 +75,12 @@ describe("checkbox.vue", () => {
|
|||
|
||||
expect(paragraph.text()).toEqual("screenreader text");
|
||||
});
|
||||
|
||||
it('Default isChecked is false', async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(checkbox, {});
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.isChecked).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
:id="buttonID"
|
||||
:tabindex="tabIndex"
|
||||
:aria-required="isRequired"
|
||||
:checked="isChecked"
|
||||
@change="handleCheckChange"
|
||||
:ref="checkboxName"
|
||||
/>
|
||||
:ref="checkboxName" />
|
||||
<label class="d-flex align-items-start" :for="buttonID">
|
||||
<p v-if="checkboxLabel" class="m-0" v-html="checkboxLabel"></p>
|
||||
<span v-if="screenReaderOnlyText" class="sr-only">{{ screenReaderOnlyText }}</span>
|
||||
|
|
@ -30,6 +30,10 @@ export default {
|
|||
screenReaderOnlyText: String,
|
||||
isRequired: Boolean,
|
||||
hasError: Boolean,
|
||||
isChecked: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleCheckChange() {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,15 @@
|
|||
<template>
|
||||
<a v-if="linkType === 'navigation'" @click="handleClick" class="navigation-link" :href="href">
|
||||
{{text}}
|
||||
<slot name="after-text"></slot>
|
||||
{{text}}<slot name="after-text"></slot>
|
||||
</a>
|
||||
<a v-else-if="linkType === 'footer'" @click="handleClick" class="footer-link" :href="href" target="_blank">
|
||||
{{text}}
|
||||
<slot name="after-text"></slot>
|
||||
{{text}}<slot name="after-text"></slot>
|
||||
</a>
|
||||
<a v-else-if="linkType === 'textSmall'" @click="handleClick" class="small" :href="href">
|
||||
{{text}}
|
||||
<slot name="after-text"></slot>
|
||||
{{text}}<slot name="after-text"></slot>
|
||||
</a>
|
||||
<a v-else-if="linkType === 'text'" @click="handleClick" :href="href">
|
||||
{{text}}
|
||||
<slot name="after-text"></slot>
|
||||
{{text}}<slot name="after-text"></slot>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue