fix: Remove validation for WhatsApp templates with no params (#4820)

This commit is contained in:
Fayaz Ahmed 2022-06-09 21:34:59 +05:30 committed by GitHub
parent 6385e42d9b
commit c5c89449dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 375 additions and 40 deletions

View file

@ -6,30 +6,28 @@
readonly readonly
class="template-input" class="template-input"
></textarea> ></textarea>
<div> <div v-if="variables" class="template__variables-container">
<div class="template__variables-container"> <p class="variables-label">
<p class="variables-label"> {{ $t('WHATSAPP_TEMPLATES.PARSER.VARIABLES_LABEL') }}
{{ $t('WHATSAPP_TEMPLATES.PARSER.VARIABLES_LABEL') }} </p>
</p> <div
<div v-for="(variable, key) in processedParams"
v-for="(variable, key) in processedParams" :key="key"
:key="key" class="template__variable-item"
class="template__variable-item" >
> <span class="variable-label">
<span class="variable-label"> {{ key }}
{{ key }} </span>
</span> <woot-input
<woot-input v-model="processedParams[key]"
v-model="processedParams[key]" type="text"
type="text" class="variable-input"
class="variable-input" :styles="{ marginBottom: 0 }"
:styles="{ marginBottom: 0 }" />
/>
</div>
<p v-if="showRequiredMessage" class="error">
{{ $t('WHATSAPP_TEMPLATES.PARSER.FORM_ERROR_MESSAGE') }}
</p>
</div> </div>
<p v-if="$v.$dirty && $v.$invalid" class="error">
{{ $t('WHATSAPP_TEMPLATES.PARSER.FORM_ERROR_MESSAGE') }}
</p>
</div> </div>
<footer> <footer>
<woot-button variant="smooth" @click="$emit('resetTemplate')"> <woot-button variant="smooth" @click="$emit('resetTemplate')">
@ -43,12 +41,11 @@
</template> </template>
<script> <script>
import { required } from 'vuelidate/lib/validators';
const allKeysRequired = value => { const allKeysRequired = value => {
const keys = Object.keys(value); const keys = Object.keys(value);
return keys.every(key => value[key]); return keys.every(key => value[key]);
}; };
import { requiredIf } from 'vuelidate/lib/validators';
export default { export default {
props: { props: {
template: { template: {
@ -58,15 +55,13 @@ export default {
}, },
validations: { validations: {
processedParams: { processedParams: {
required, requiredIfKeysPresent: requiredIf('variables'),
allKeysRequired, allKeysRequired,
}, },
}, },
data() { data() {
return { return {
message: this.template.message,
processedParams: {}, processedParams: {},
showRequiredMessage: false,
}; };
}, },
computed: { computed: {
@ -92,11 +87,8 @@ export default {
methods: { methods: {
sendMessage() { sendMessage() {
this.$v.$touch(); this.$v.$touch();
if (this.$v.$invalid) { if (this.$v.$invalid) return;
this.showRequiredMessage = true; const payload = {
return;
}
const message = {
message: this.processedString, message: this.processedString,
templateParams: { templateParams: {
name: this.template.name, name: this.template.name,
@ -106,18 +98,16 @@ export default {
processed_params: this.processedParams, processed_params: this.processedParams,
}, },
}; };
this.$emit('sendMessage', message); this.$emit('sendMessage', payload);
}, },
processVariable(str) { processVariable(str) {
return str.replace(/{{|}}/g, ''); return str.replace(/{{|}}/g, '');
}, },
generateVariables() { generateVariables() {
const templateString = this.template.components.find( const matchedVariables = this.templateString.match(/{{([^}]+)}}/g);
component => component.type === 'BODY' if (!matchedVariables) return;
).text;
const variables = templateString.match(/{{([^}]+)}}/g).map(variable => { const variables = matchedVariables.map(i => this.processVariable(i));
return this.processVariable(variable);
});
this.processedParams = variables.reduce((acc, variable) => { this.processedParams = variables.reduce((acc, variable) => {
acc[variable] = ''; acc[variable] = '';
return acc; return acc;

View file

@ -0,0 +1,268 @@
export const templates = [
{
name: 'sample_flight_confirmation',
status: 'approved',
category: 'TICKET_UPDATE',
language: 'pt_BR',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{ type: 'HEADER', format: 'DOCUMENT' },
{
text: 'Esta é a sua confirmação de voo para {{1}}-{{2}} em {{3}}.',
type: 'BODY',
},
{
text: 'Esta mensagem é de uma empresa não verificada.',
type: 'FOOTER',
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_issue_resolution',
status: 'approved',
category: 'ISSUE_RESOLUTION',
language: 'pt_BR',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
text:
'Oi, {{1}}. Nós conseguimos resolver o problema que você estava enfrentando?',
type: 'BODY',
},
{
text: 'Esta mensagem é de uma empresa não verificada.',
type: 'FOOTER',
},
{
type: 'BUTTONS',
buttons: [
{ text: 'Sim', type: 'QUICK_REPLY' },
{ text: 'Não', type: 'QUICK_REPLY' },
],
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_issue_resolution',
status: 'approved',
category: 'ISSUE_RESOLUTION',
language: 'es',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
text: 'Hola, {{1}}. ¿Pudiste solucionar el problema que tenías?',
type: 'BODY',
},
{
text: 'Este mensaje proviene de un negocio no verificado.',
type: 'FOOTER',
},
{
type: 'BUTTONS',
buttons: [
{ text: 'Sí', type: 'QUICK_REPLY' },
{ text: 'No', type: 'QUICK_REPLY' },
],
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_issue_resolution',
status: 'approved',
category: 'ISSUE_RESOLUTION',
language: 'id',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
text:
'Halo {{1}}, apakah kami bisa mengatasi masalah yang sedang Anda hadapi?',
type: 'BODY',
},
{
text: 'Pesan ini berasal dari bisnis yang tidak terverifikasi.',
type: 'FOOTER',
},
{
type: 'BUTTONS',
buttons: [
{ text: 'Ya', type: 'QUICK_REPLY' },
{ text: 'Tidak', type: 'QUICK_REPLY' },
],
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_shipping_confirmation',
status: 'approved',
category: 'SHIPPING_UPDATE',
language: 'pt_BR',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
text: 'Seu pacote foi enviado. Ele será entregue em {{1}} dias úteis.',
type: 'BODY',
},
{
text: 'Esta mensagem é de uma empresa não verificada.',
type: 'FOOTER',
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_shipping_confirmation',
status: 'approved',
category: 'SHIPPING_UPDATE',
language: 'id',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
text:
'Paket Anda sudah dikirim. Paket akan sampai dalam {{1}} hari kerja.',
type: 'BODY',
},
{
text: 'Pesan ini berasal dari bisnis yang tidak terverifikasi.',
type: 'FOOTER',
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_shipping_confirmation',
status: 'approved',
category: 'SHIPPING_UPDATE',
language: 'es',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
text: 'ó tu paquete. La entrega se realizará en {{1}} dí.',
type: 'BODY',
},
{
text: 'Este mensaje proviene de un negocio no verificado.',
type: 'FOOTER',
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_flight_confirmation',
status: 'approved',
category: 'TICKET_UPDATE',
language: 'id',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{ type: 'HEADER', format: 'DOCUMENT' },
{
text:
'Ini merupakan konfirmasi penerbangan Anda untuk {{1}}-{{2}} di {{3}}.',
type: 'BODY',
},
{
text: 'Pesan ini berasal dari bisnis yang tidak terverifikasi.',
type: 'FOOTER',
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_issue_resolution',
status: 'approved',
category: 'ISSUE_RESOLUTION',
language: 'en_US',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
text: 'Hi {{1}}, were we able to solve the issue that you were facing?',
type: 'BODY',
},
{ text: 'This message is from an unverified business.', type: 'FOOTER' },
{
type: 'BUTTONS',
buttons: [
{ text: 'Yes', type: 'QUICK_REPLY' },
{ text: 'No', type: 'QUICK_REPLY' },
],
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_flight_confirmation',
status: 'approved',
category: 'TICKET_UPDATE',
language: 'es',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{ type: 'HEADER', format: 'DOCUMENT' },
{
text: 'Confirmamos tu vuelo a {{1}}-{{2}} para el {{3}}.',
type: 'BODY',
},
{
text: 'Este mensaje proviene de un negocio no verificado.',
type: 'FOOTER',
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_flight_confirmation',
status: 'approved',
category: 'TICKET_UPDATE',
language: 'en_US',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{ type: 'HEADER', format: 'DOCUMENT' },
{
text: 'This is your flight confirmation for {{1}}-{{2}} on {{3}}.',
type: 'BODY',
},
{ text: 'This message is from an unverified business.', type: 'FOOTER' },
],
rejected_reason: 'NONE',
},
{
name: 'sample_shipping_confirmation',
status: 'approved',
category: 'SHIPPING_UPDATE',
language: 'en_US',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
text:
'Your package has been shipped. It will be delivered in {{1}} business days.',
type: 'BODY',
},
{ text: 'This message is from an unverified business.', type: 'FOOTER' },
],
rejected_reason: 'NONE',
},
{
name: 'no_variable_template',
status: 'approved',
category: 'TICKET_UPDATE',
language: 'pt_BR',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
type: 'HEADER',
format: 'DOCUMENT',
},
{
text: 'This is a test whatsapp template',
type: 'BODY',
},
{
text: 'Esta mensagem é de uma empresa não verificada.',
type: 'FOOTER',
},
],
rejected_reason: 'NONE',
},
];

View file

@ -0,0 +1,77 @@
import TemplateParser from '../../../../dashboard/components/widgets/conversation/WhatsappTemplates/TemplateParser.vue';
import { mount, createLocalVue } from '@vue/test-utils';
import { templates } from './fixtures';
const localVue = createLocalVue();
import VueI18n from 'vue-i18n';
import Vue from 'vue';
import Vuelidate from 'vuelidate';
Vue.use(Vuelidate);
import i18n from 'dashboard/i18n';
localVue.use(VueI18n);
const i18nConfig = new VueI18n({
locale: 'en',
messages: i18n,
});
describe('#WhatsAppTemplates', () => {
it('returns all variables from a template string', () => {
const wrapper = mount(TemplateParser, {
localVue,
propsData: {
template: templates[0],
},
i18n: i18nConfig,
});
expect(wrapper.vm.variables).toEqual(['{{1}}', '{{2}}', '{{3}}']);
});
it('returns no variables from a template string if it does not contain variables', () => {
const wrapper = mount(TemplateParser, {
localVue,
propsData: {
template: templates[12],
},
i18n: i18nConfig,
});
expect(wrapper.vm.variables).toBeNull();
});
it('returns the body of a template', () => {
const wrapper = mount(TemplateParser, {
localVue,
propsData: {
template: templates[1],
},
i18n: i18nConfig,
});
const expectedOutput = templates[1].components.find(i => i.type === 'BODY')
.text;
expect(wrapper.vm.templateString).toEqual(expectedOutput);
});
it('generates the templates from variable input', async () => {
const wrapper = mount(TemplateParser, {
localVue,
propsData: {
template: templates[0],
},
data: () => {
return {
processedParams: {},
};
},
i18n: i18nConfig,
});
await wrapper.setData({
processedParams: { '1': 'abc', '2': 'xyz', '3': 'qwerty' },
});
await wrapper.vm.$nextTick();
const expectedOutput =
'Esta é a sua confirmação de voo para abc-xyz em qwerty.';
expect(wrapper.vm.processedString).toEqual(expectedOutput);
});
});