Adding tests for textarea question component
This commit is contained in:
parent
51daeb0028
commit
a1f5d577a0
5 changed files with 376 additions and 25 deletions
|
|
@ -1 +1,348 @@
|
||||||
// TODO finish
|
// Components
|
||||||
|
import textareaQuestion from '@/digital-components/textarea-question/textarea-question.vue';
|
||||||
|
|
||||||
|
// Supporting Files
|
||||||
|
import { shallowMount } from '@vue/test-utils';
|
||||||
|
import { getRandomString, getRandomInt } from '@/helpers/data-generation';
|
||||||
|
import { nextTick } from 'vue';
|
||||||
|
import { defineRule } from 'vee-validate';
|
||||||
|
import { required } from '@/helpers/validation-rules';
|
||||||
|
|
||||||
|
describe('textarea-question.vue', () => {
|
||||||
|
const mockMixin = {
|
||||||
|
methods: {
|
||||||
|
getCmsContent: jest.fn().mockImplementation(() =>
|
||||||
|
getRandomString(10, 20)),
|
||||||
|
setCmsContent: jest.fn()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
describe('Expected components rendered', () => {
|
||||||
|
test('Label rendered', () => {
|
||||||
|
// Arrange
|
||||||
|
const questionText = getRandomString(10, 20);
|
||||||
|
const mixinWithQuestion = {
|
||||||
|
methods: {
|
||||||
|
getCmsContent: jest.fn().mockImplementation(() =>
|
||||||
|
questionText),
|
||||||
|
setCmsContent: jest.fn()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20)
|
||||||
|
},
|
||||||
|
mixins: [mixinWithQuestion]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const label = wrapper.find('label');
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(label.exists()).toBe(true);
|
||||||
|
expect(wrapper.text()).toContain(questionText);
|
||||||
|
});
|
||||||
|
test('Textarea rendered', () => {
|
||||||
|
// Arrange
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20)
|
||||||
|
},
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const textarea = wrapper.find('textarea');
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(textarea.exists()).toBe(true);
|
||||||
|
});
|
||||||
|
test('Character count rendered', () => {
|
||||||
|
// Arrange
|
||||||
|
const maxLength = getRandomInt(100, 900);
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20),
|
||||||
|
maxLength
|
||||||
|
},
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
const expected = `0/${maxLength}`;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.text()).toContain(expected);
|
||||||
|
});
|
||||||
|
test('Error rendered', async () => {
|
||||||
|
// Arrange
|
||||||
|
const maxLength = getRandomInt(100, 900);
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20),
|
||||||
|
maxLength
|
||||||
|
},
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const errorComponent = wrapper.find('#error-message');
|
||||||
|
expect(errorComponent.text()).toBe('');
|
||||||
|
|
||||||
|
const errorMessage = getRandomString(20, 30);
|
||||||
|
wrapper.vm.resetField({
|
||||||
|
errors: [errorMessage]
|
||||||
|
});
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(errorComponent.text()).toBe(errorMessage);
|
||||||
|
});
|
||||||
|
test('Not required => optional text rendered', () => {
|
||||||
|
// Arrange
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20),
|
||||||
|
isRequired: false
|
||||||
|
},
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.text()).toContain('Optional');
|
||||||
|
});
|
||||||
|
test('Required => optional text not rendered', () => {
|
||||||
|
// Arrange
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20),
|
||||||
|
isRequired: true
|
||||||
|
},
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.text()).not.toContain('Optional');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('Computed properties rendered', () => {
|
||||||
|
describe('characterCount', () => {
|
||||||
|
test('Model value null => character count is 0 ', () => {
|
||||||
|
// Arrange
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20),
|
||||||
|
modelValue: null
|
||||||
|
},
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.vm.characterCount).toBe(0);
|
||||||
|
});
|
||||||
|
test('Model value undefined => character count is 0', () => {
|
||||||
|
// Arrange
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20),
|
||||||
|
modelValue: undefined
|
||||||
|
},
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.vm.characterCount).toBe(0);
|
||||||
|
});
|
||||||
|
test('Model value empty string => character count is 0', () => {
|
||||||
|
// Arrange
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20),
|
||||||
|
modelValue: ''
|
||||||
|
},
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.vm.characterCount).toBe(0);
|
||||||
|
});
|
||||||
|
test('Model value non-empty string => character count is as expected', () => {
|
||||||
|
// Arrange
|
||||||
|
const length = getRandomInt(10, 30);
|
||||||
|
const value = getRandomString(length, length);
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20),
|
||||||
|
modelValue: value
|
||||||
|
},
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.vm.characterCount).toBe(length);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('value', () => {
|
||||||
|
test('Get value corresponds to modelValue', () => {
|
||||||
|
// Arrange
|
||||||
|
const modelValue = getRandomString(10, 20);
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20),
|
||||||
|
modelValue
|
||||||
|
},
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.vm.value).toBe(modelValue);
|
||||||
|
});
|
||||||
|
test('Set value => update modelValue event emitted with new value', () => {
|
||||||
|
// Arrange
|
||||||
|
const modelValue = getRandomString(10, 20);
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20),
|
||||||
|
modelValue
|
||||||
|
},
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
const newValue = getRandomString(10, 20);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
wrapper.vm.value = newValue;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(wrapper.emitted('update:modelValue')).toBeTruthy();
|
||||||
|
expect(wrapper.emitted('update:modelValue')[0][0]).toEqual(newValue);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('Properties properly affect initial state', () => {
|
||||||
|
test('Placeholder text sets placeholder text of text area', () => {
|
||||||
|
// Arrange
|
||||||
|
const placeholderText = getRandomString(10, 20);
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20),
|
||||||
|
placeholderText
|
||||||
|
},
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const textarea = wrapper.find('textarea');
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(textarea.attributes().placeholder).toBe(placeholderText);
|
||||||
|
});
|
||||||
|
test('isDisabled true => textarea disabled', () => {
|
||||||
|
// Arrange
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20),
|
||||||
|
isDisabled: true
|
||||||
|
},
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const textarea = wrapper.find('textarea');
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(textarea.attributes().disabled).toBeDefined();
|
||||||
|
});
|
||||||
|
test('isRequired true => textarea required', () => {
|
||||||
|
// Arrange
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20),
|
||||||
|
isRequired: true
|
||||||
|
},
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const textarea = wrapper.find('textarea');
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(textarea.attributes().required).toBeDefined();
|
||||||
|
});
|
||||||
|
test('validationRules set rules of text area', () => {
|
||||||
|
// Arrange
|
||||||
|
const rule = getRandomString(10, 20);
|
||||||
|
const errorMessage = getRandomString(20, 30);
|
||||||
|
defineRule(rule, required(errorMessage));
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20),
|
||||||
|
validationRules: rule
|
||||||
|
},
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const textarea = wrapper.find('textarea');
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(textarea.attributes().validationrules).toBe(rule);
|
||||||
|
});
|
||||||
|
test('maxLength set max length of text area', () => {
|
||||||
|
// Arrange
|
||||||
|
const maxLength = toString(getRandomInt(100, 900));
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20),
|
||||||
|
maxLength
|
||||||
|
},
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const textarea = wrapper.find('textarea');
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(textarea.attributes().maxlength).toBe(maxLength);
|
||||||
|
});
|
||||||
|
test('inputRows set input rows of text area', () => {
|
||||||
|
// Arrange
|
||||||
|
const inputRows = getRandomInt(10, 20);
|
||||||
|
const wrapper = shallowMount(textareaQuestion, {
|
||||||
|
propsData: {
|
||||||
|
inputId: getRandomString(10, 20),
|
||||||
|
cmsWidgetName: getRandomString(10, 20),
|
||||||
|
inputRows
|
||||||
|
},
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const textarea = wrapper.find('textarea');
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(textarea.attributes().rows).toBe(`${inputRows}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="textarea-question"
|
class="textarea-question"
|
||||||
:class="(errors && errors.length) || hasError ? 'has-error' : ''">
|
:class="(errors && errors.length) ? 'has-error' : ''">
|
||||||
<label
|
<label
|
||||||
:for="inputId"
|
:for="inputId"
|
||||||
class="form-label">
|
class="form-label">
|
||||||
|
|
@ -17,12 +17,14 @@
|
||||||
v-model.trim="value"
|
v-model.trim="value"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:name="inputId"
|
:name="inputId"
|
||||||
:rows="[inputRows ?? 3]"
|
:rows="[inputRows ?? 10]"
|
||||||
:placeholder="placeholderText"
|
:placeholder="placeholderText"
|
||||||
:disabled="isDisabled"
|
:disabled="isDisabled"
|
||||||
:required="isRequired"
|
:required="isRequired"
|
||||||
:maxlength="maxLength"
|
:maxlength="maxLength"
|
||||||
:validationRules="validationRules"
|
:validationRules="validationRules"
|
||||||
|
@change="validationRules ? handleChange : () => {}"
|
||||||
|
@blur="validationRules ? handleBlur : () => {}"
|
||||||
@paste="trimOnPaste"
|
@paste="trimOnPaste"
|
||||||
@drop="trimOnPaste">
|
@drop="trimOnPaste">
|
||||||
</textarea>
|
</textarea>
|
||||||
|
|
@ -32,6 +34,7 @@
|
||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
v-show="errorMessage"
|
v-show="errorMessage"
|
||||||
|
id="error-message"
|
||||||
class="row my-1 form-test-error">
|
class="row my-1 form-test-error">
|
||||||
<span
|
<span
|
||||||
class="d-inline-flex mt-0"
|
class="d-inline-flex mt-0"
|
||||||
|
|
@ -58,13 +61,18 @@ export default {
|
||||||
modelValue: String,
|
modelValue: String,
|
||||||
isDisabled: Boolean,
|
isDisabled: Boolean,
|
||||||
isRequired: Boolean,
|
isRequired: Boolean,
|
||||||
hasError: Boolean,
|
|
||||||
validationRules: String,
|
validationRules: String,
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: {
|
||||||
maxLength: String,
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
maxLength: {
|
||||||
|
type: String,
|
||||||
|
default: '300'
|
||||||
|
},
|
||||||
inputRows: Number
|
inputRows: Number
|
||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'textareaQuestionEvent.inputIdAssigned'],
|
emits: ['update:modelValue'],
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const propsClone = { ...props };
|
const propsClone = { ...props };
|
||||||
const { modelValue } = propsClone;
|
const { modelValue } = propsClone;
|
||||||
|
|
@ -78,8 +86,10 @@ export default {
|
||||||
|
|
||||||
const { errorMessage,
|
const { errorMessage,
|
||||||
handleChange,
|
handleChange,
|
||||||
|
handleBlur,
|
||||||
validate,
|
validate,
|
||||||
errors }
|
errors,
|
||||||
|
resetField }
|
||||||
= useField(props.inputId,
|
= useField(props.inputId,
|
||||||
props.validationRules,
|
props.validationRules,
|
||||||
fieldOptions);
|
fieldOptions);
|
||||||
|
|
@ -87,8 +97,10 @@ export default {
|
||||||
return {
|
return {
|
||||||
errorMessage,
|
errorMessage,
|
||||||
handleChange,
|
handleChange,
|
||||||
|
handleBlur,
|
||||||
validate,
|
validate,
|
||||||
errors
|
errors,
|
||||||
|
resetField
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -107,18 +119,6 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
async value(newValue) {
|
|
||||||
const result = await this.validate(newValue, this.validationRules);
|
|
||||||
if (result.valid) {
|
|
||||||
this.handleChange(newValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
// TODO do we need to do anything else for this event?
|
|
||||||
this.$emit('textareaQuestionEvent.inputIdAssigned', this.inputId);
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
trimOnPaste(evt) {
|
trimOnPaste(evt) {
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
|
|
@ -147,7 +147,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control {
|
.form-control {
|
||||||
max-height: 5rem;
|
max-height: 30rem;
|
||||||
border: 1px solid $gray-500;
|
border: 1px solid $gray-500;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
|
|
|
||||||
|
|
@ -62,10 +62,9 @@
|
||||||
inputId="technicianNotes"
|
inputId="technicianNotes"
|
||||||
:isDisabled="false"
|
:isDisabled="false"
|
||||||
:isRequired="false"
|
:isRequired="false"
|
||||||
hasError="hasError"
|
|
||||||
:cmsWidgetName="widget.notesQuestion"
|
:cmsWidgetName="widget.notesQuestion"
|
||||||
maxLength="500"
|
maxLength="500"
|
||||||
inputRows="30" />
|
:inputRows="3" />
|
||||||
|
|
||||||
<p
|
<p
|
||||||
ref="disclaimerText"
|
ref="disclaimerText"
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import { issPageValues } from "@/router/router-constants/issPage-values";
|
||||||
import { Form } from "vee-validate";
|
import { Form } from "vee-validate";
|
||||||
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
|
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
|
||||||
import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
import BaseFormMixin from '@/mixins/base-form-mixin.js';
|
||||||
import globalRules from '@/helpers/global-rules';
|
import globalRules from '@/constants/global-rules';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "part-questions",
|
name: "part-questions",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,10 @@ import {vinLookupMethodSelections} from '@/constants/vin-lookup-methods';
|
||||||
import { useMainStore } from '@/store';
|
import { useMainStore } from '@/store';
|
||||||
import { createTestingPinia } from '@pinia/testing';
|
import { createTestingPinia } from '@pinia/testing';
|
||||||
import { mapStores } from "pinia";
|
import { mapStores } from "pinia";
|
||||||
|
import { defineRule } from 'vee-validate';
|
||||||
|
import { errorMessages } from '@/constants/error-messages';
|
||||||
|
import globalRules from '@/constants/global-rules';
|
||||||
|
import { required } from '@/helpers/validation-rules';
|
||||||
|
|
||||||
const pinia = createTestingPinia();
|
const pinia = createTestingPinia();
|
||||||
useMainStore(pinia);
|
useMainStore(pinia);
|
||||||
|
|
@ -44,6 +48,7 @@ const siteFooterWidgetMockData = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function setupMocks() {
|
function setupMocks() {
|
||||||
|
defineRule(globalRules.OPTION_REQUIRED, required(errorMessages.OPTION_REQUIRED));
|
||||||
const mockRoute = {
|
const mockRoute = {
|
||||||
query: {
|
query: {
|
||||||
issPage: 'vehicle-lookup',
|
issPage: 'vehicle-lookup',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue