Merge pull request #363 from Safelite/feature/digital/SSR-494

Feature/digital/ssr 494
This commit is contained in:
michaela-brydon-safelite 2023-07-12 15:02:19 -04:00 committed by GitHub
commit 8c78204602
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 1495 additions and 1399 deletions

View file

@ -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

View file

@ -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);
}
}
};

View file

@ -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
});
});
});
});

View file

@ -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);
}

File diff suppressed because it is too large Load diff

View file

@ -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('');
});
});
});

View file

@ -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);
});
});

View file

@ -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() {

View file

@ -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>