Skip to content

Commit 6a4524d

Browse files
authored
Merge pull request #3063 from glific/templates-refactoring
Refactoring: Separating HSM and Speed sends
2 parents f061508 + 78a5145 commit 6a4524d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+3189
-3578
lines changed

.github/workflows/cypress-testing.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ jobs:
9696
git clone https://github.com/glific/cypress-testing.git
9797
echo done. go to dir.
9898
cd cypress-testing
99-
git checkout main
99+
git checkout templates
100100
cd ..
101101
cp -r cypress-testing/cypress cypress
102102

src/components/UI/Form/AutoComplete/AutoComplete.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,9 @@ export const AutoComplete = ({
178178
freeSolo={freeSolo}
179179
autoSelect={autoSelect}
180180
disableClearable={disableClearable}
181-
getOptionLabel={(option: any) => (option[optionLabel] != null ? option[optionLabel] : option)}
181+
getOptionLabel={(option: any) =>
182+
option[optionLabel] != null ? option[optionLabel] : typeof option === 'string' ? option : ''
183+
}
182184
getOptionDisabled={getOptionDisabled}
183185
isOptionEqualToValue={(option, value) => {
184186
if (value) {

src/containers/Chat/ChatMessages/AddToMessageTemplate/AddToMessageTemplate.test.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import { render, fireEvent, cleanup, waitFor } from '@testing-library/react';
22
import { MockedProvider } from '@apollo/client/testing';
33

44
import { SAVE_MESSAGE_TEMPLATE_MUTATION } from 'graphql/mutations/MessageTemplate';
5-
import { TEMPLATE_MOCKS } from 'mocks/Template';
5+
import { filterTemplatesQuery } from 'mocks/Template';
66
import AddToMessageTemplate from './AddToMessageTemplate';
77

88
let resultReturned = false;
99

1010
const mocks = [
11-
...TEMPLATE_MOCKS,
11+
filterTemplatesQuery('', {}),
1212
{
1313
request: {
1414
query: SAVE_MESSAGE_TEMPLATE_MUTATION,

src/containers/Chat/ChatMessages/ChatInput/ChatInput.test.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { render, waitFor, fireEvent, screen } from '@testing-library/react';
33
import { vi } from 'vitest';
44

55
import ChatInput from './ChatInput';
6-
import { TEMPLATE_MOCKS } from 'mocks/Template';
76
import { createMediaMessageMock, getAttachmentPermissionMock, uploadBlobMock } from 'mocks/Attachment';
87
import { searchInteractive, searchInteractiveHi } from 'mocks/InteractiveMessage';
98
import '../VoiceRecorder/VoiceRecorder';
109
import { LexicalWrapper } from 'common/LexicalWrapper';
10+
import { TEMPLATE_MOCKS } from 'mocks/Template';
1111

1212
const mocks = [
1313
searchInteractive,

src/containers/Chat/ChatMessages/ChatTemplates/ChatTemplates.test.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { MockedProvider } from '@apollo/client/testing';
22
import ChatTemplates from './ChatTemplates';
3-
import { TEMPLATE_MOCKS } from '../../../../mocks/Template';
43
import { render, fireEvent, waitFor } from '@testing-library/react';
4+
import { TEMPLATE_MOCKS } from 'mocks/Template';
55

66
const mocks = TEMPLATE_MOCKS;
77

src/containers/Chat/ChatMessages/TemplateButtons/TemplateButtons.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export const TemplateButtons = ({
3131
onClick={() => handleButtonClick(type, value)}
3232
startIcon={icon}
3333
disabled={!isSimulator}
34+
data-testid="templateButton"
3435
>
3536
{title}
3637
</Button>

src/containers/Form/FormLayout.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ export const FormLayout = ({
179179
}
180180
});
181181
// for template create media for attachment
182-
if (isAttachment && payload.type !== 'TEXT' && payload.type) {
182+
if (isAttachment && payload.type !== 'TEXT' && payload.type && !entityId) {
183183
getMediaId(payload)
184184
.then((data: any) => {
185185
if (data) {

src/containers/HSM/HSM.helper.ts

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { CALL_TO_ACTION, MEDIA_MESSAGE_TYPES, QUICK_REPLY } from 'common/constants';
2+
3+
export interface CallToActionTemplate {
4+
type: string;
5+
title: string;
6+
value: string;
7+
}
8+
9+
export interface QuickReplyTemplate {
10+
value: string;
11+
}
12+
13+
export const mediaOptions = MEDIA_MESSAGE_TYPES.map((option: string) => ({ id: option, label: option })).filter(
14+
({ label }) => label !== 'AUDIO' && label !== 'STICKER'
15+
);
16+
17+
export const removeFirstLineBreak = (text: any) =>
18+
text?.length === 1 ? text.slice(0, 1).replace(/(\r\n|\n|\r)/, '') : text;
19+
20+
/**
21+
* Function to convert buttons to template format
22+
*
23+
* @param templateButtons buttons that need to be converted to gupshup format
24+
* @param templateType depending on template type convert button to gupshup format
25+
*
26+
* @return array result
27+
*/
28+
export const convertButtonsToTemplate = (templateButtons: Array<any>, templateType: string | null) =>
29+
templateButtons.reduce((result: any, temp: any) => {
30+
const { title, value } = temp;
31+
if (templateType === CALL_TO_ACTION && value && title) {
32+
result.push(`[${title}, ${value}]`);
33+
}
34+
if (templateType === QUICK_REPLY && value) {
35+
result.push(`[${value}]`);
36+
}
37+
return result;
38+
}, []);
39+
40+
/**
41+
* As messages and buttons are now separated
42+
* we are combining both message and buttons,
43+
* so that you can see preview in simulator
44+
*
45+
* @param templateType template type
46+
* @param message
47+
* @param buttons
48+
*
49+
* @return object {buttons, template}
50+
*/
51+
export const getTemplateAndButtons = (templateType: string, message: string, buttons: string) => {
52+
const templateButtons = JSON.parse(buttons);
53+
let result: any;
54+
if (templateType === CALL_TO_ACTION) {
55+
result = templateButtons.map((button: any) => {
56+
const { phone_number: phoneNo, url, type, text } = button;
57+
return { type, value: url || phoneNo, title: text };
58+
});
59+
}
60+
61+
if (templateType === QUICK_REPLY) {
62+
result = templateButtons.map((button: any) => {
63+
const { text, type } = button;
64+
return { type, value: text };
65+
});
66+
}
67+
68+
// Getting in template format of gupshup
69+
const templateFormat = convertButtonsToTemplate(result, templateType);
70+
// Pre-pending message with buttons
71+
const template = `${message} | ${templateFormat.join(' | ')}`;
72+
return { buttons: result, template };
73+
};
74+
75+
export const getExampleFromBody = (body: string, variables: Array<any>) => {
76+
return body.replace(/{{(\d+)}}/g, (match, number) => {
77+
let index = parseInt(number) - 1;
78+
79+
return variables[index]?.text ? (variables[index] ? `[${variables[index]?.text}]` : match) : `{{${number}}}`;
80+
});
81+
};
82+
83+
export const getVariables = (message: string, variables: any) => {
84+
const regex = /{{\d+}}/g;
85+
const matches = message.match(regex);
86+
87+
if (!matches) {
88+
return [];
89+
}
90+
91+
return matches.map((match, index) => (variables[index]?.text ? variables[index] : { text: '', id: index + 1 }));
92+
};
93+
94+
export const getExampleValue = (example: string) => {
95+
const regex = /\[([^\]]+)\]/g;
96+
let match;
97+
const variables = [];
98+
let id = 1;
99+
100+
while ((match = regex.exec(example)) !== null) {
101+
variables.push({ text: match[1], id });
102+
id++;
103+
}
104+
105+
return variables;
106+
};

src/containers/HSM/HSM.module.css

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
.Template {
2+
margin: 20px auto;
3+
width: 80%;
4+
text-align: center;
5+
box-shadow: 0 2px 3px #cccccc;
6+
border: 1px solid #eeeeee;
7+
padding: 10px;
8+
box-sizing: border-box;
9+
}
10+
11+
@media (min-width: 600px) {
12+
.Template {
13+
width: 500px;
14+
}
15+
}
16+
17+
.DeleteIcon {
18+
margin-right: 9px !important;
19+
}
20+
21+
.DialogText {
22+
margin-top: 0px;
23+
text-align: center;
24+
color: #073f24;
25+
font-weight: 400;
26+
}
27+
28+
.DeleteButton {
29+
margin-left: auto !important;
30+
}
31+
32+
.Title {
33+
margin-left: 24px !important;
34+
margin-top: 16px !important;
35+
vertical-align: middle;
36+
font-weight: 500 !important;
37+
color: #073f24;
38+
}
39+
40+
.Input {
41+
display: flex;
42+
padding: 8px;
43+
}
44+
45+
.Label {
46+
width: 50%;
47+
align-self: center;
48+
font-weight: bold;
49+
}
50+
51+
.TemplateAdd {
52+
width: fit-content;
53+
}
54+
55+
.Form {
56+
padding: 16px 16px;
57+
width: 470px;
58+
}
59+
60+
.Buttons {
61+
margin-top: 24px;
62+
margin-left: 8px;
63+
display: flex;
64+
justify-content: flex-start;
65+
}
66+
67+
.Icon {
68+
background-color: #eaedec !important;
69+
margin-right: 10px !important;
70+
}
71+
72+
.ButtonsCenter {
73+
justify-content: center !important;
74+
}
75+
76+
.Button {
77+
margin-right: 24px !important;
78+
}
79+
80+
.Warning {
81+
color: #ff0000;
82+
margin-left: -43px;
83+
}
84+
85+
.IsActive {
86+
color: #555555;
87+
font-weight: 400;
88+
line-height: 18px;
89+
font-size: 16px;
90+
}
91+
92+
.TemplateIcon {
93+
width: 29px;
94+
height: 29px;
95+
}
96+
97+
.Checkbox {
98+
color: #555555;
99+
font-weight: 400;
100+
line-height: 18px;
101+
font-size: 16px;
102+
}

src/containers/Template/Form/HSM/HSM.test.tsx src/containers/HSM/HSM.test.tsx

+37-12
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
1-
import { render, waitFor, within, fireEvent, screen } from '@testing-library/react';
1+
import { render, waitFor, within, fireEvent, screen, cleanup } from '@testing-library/react';
22
import { MockedProvider } from '@apollo/client/testing';
33
import userEvent from '@testing-library/user-event';
44
import { MemoryRouter, Route, Routes } from 'react-router-dom';
55
import { HSM } from './HSM';
6-
import {
7-
TEMPLATE_MOCKS,
8-
getHSMTemplateTypeMedia,
9-
getHSMTemplateTypeText,
10-
} from 'containers/Template/Template.test.helper';
6+
import { HSM_TEMPLATE_MOCKS, getHSMTemplateTypeMedia, getHSMTemplateTypeText } from 'mocks/Template';
7+
import { setNotification } from 'common/notification';
118

12-
const mocks = TEMPLATE_MOCKS;
9+
const mocks = HSM_TEMPLATE_MOCKS;
10+
11+
vi.mock('common/notification', async (importOriginal) => {
12+
const mod = await importOriginal<typeof import('common/notification')>();
13+
return {
14+
...mod,
15+
setNotification: vi.fn((...args) => {
16+
return args[1];
17+
}),
18+
};
19+
});
1320

1421
beforeEach(() => {
15-
vi.restoreAllMocks();
22+
cleanup();
1623
});
1724

1825
vi.mock('lexical-beautiful-mentions', async (importOriginal) => {
@@ -80,6 +87,7 @@ describe('Add mode', () => {
8087
</MemoryRouter>
8188
</MockedProvider>
8289
);
90+
8391
const user = userEvent.setup();
8492

8593
test('check for validations for the HSM form', async () => {
@@ -120,7 +128,6 @@ describe('Add mode', () => {
120128
const inputs = screen.getAllByRole('textbox');
121129

122130
fireEvent.change(inputs[0], { target: { value: 'element_name' } });
123-
fireEvent.change(inputs[1], { target: { value: 'element_name' } });
124131
const lexicalEditor = inputs[2];
125132

126133
await user.click(lexicalEditor);
@@ -144,11 +151,19 @@ describe('Add mode', () => {
144151
await waitFor(() => {
145152
expect(screen.getByText('Hi, How are you {{1}}')).toBeInTheDocument();
146153
});
147-
fireEvent.change(inputs[1], { target: { value: 'element_name' } });
148154

149155
fireEvent.change(screen.getByPlaceholderText('Define value'), { target: { value: 'User' } });
150156

157+
autocompletes[3].focus();
158+
fireEvent.keyDown(autocompletes[3], { key: 'ArrowDown' });
159+
fireEvent.click(screen.getByText('Messages'), { key: 'Enter' });
160+
fireEvent.change(inputs[1], { target: { value: 'title' } });
161+
151162
fireEvent.click(screen.getByTestId('submitActionButton'));
163+
164+
await waitFor(() => {
165+
expect(setNotification).toHaveBeenCalled();
166+
});
152167
});
153168

154169
test('it should add and remove variables', async () => {
@@ -176,6 +191,10 @@ describe('Add mode', () => {
176191
});
177192

178193
fireEvent.click(screen.getAllByTestId('delete-variable')[0]);
194+
195+
await waitFor(() => {
196+
expect(screen.queryByPlaceholderText('Define value ')).not.toBeInTheDocument();
197+
});
179198
});
180199

181200
test('it adds quick reply buttons', async () => {
@@ -224,7 +243,10 @@ describe('Add mode', () => {
224243
fireEvent.click(screen.getByText('ACCOUNT_UPDATE'), { key: 'Enter' });
225244

226245
fireEvent.click(screen.getByTestId('submitActionButton'));
227-
fireEvent.click(screen.getByTestId('submitActionButton'));
246+
247+
await waitFor(() => {
248+
expect(setNotification).toHaveBeenCalled();
249+
});
228250
});
229251

230252
test('it adds call to action buttons', async () => {
@@ -272,7 +294,10 @@ describe('Add mode', () => {
272294
fireEvent.click(screen.getByText('ACCOUNT_UPDATE'), { key: 'Enter' });
273295

274296
fireEvent.click(screen.getByTestId('submitActionButton'));
275-
fireEvent.click(screen.getByTestId('submitActionButton'));
297+
298+
await waitFor(() => {
299+
expect(setNotification).toHaveBeenCalled();
300+
});
276301
});
277302

278303
test('adding attachments', async () => {

0 commit comments

Comments
 (0)