Skip to content

Commit

Permalink
Merge pull request #3063 from glific/templates-refactoring
Browse files Browse the repository at this point in the history
Refactoring: Separating HSM and Speed sends
  • Loading branch information
kurund authored Jan 3, 2025
2 parents f061508 + 78a5145 commit 6a4524d
Show file tree
Hide file tree
Showing 43 changed files with 3,189 additions and 3,578 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cypress-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ jobs:
git clone https://github.com/glific/cypress-testing.git
echo done. go to dir.
cd cypress-testing
git checkout main
git checkout templates
cd ..
cp -r cypress-testing/cypress cypress
yarn add [email protected]
Expand Down
4 changes: 3 additions & 1 deletion src/components/UI/Form/AutoComplete/AutoComplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,9 @@ export const AutoComplete = ({
freeSolo={freeSolo}
autoSelect={autoSelect}
disableClearable={disableClearable}
getOptionLabel={(option: any) => (option[optionLabel] != null ? option[optionLabel] : option)}
getOptionLabel={(option: any) =>
option[optionLabel] != null ? option[optionLabel] : typeof option === 'string' ? option : ''
}
getOptionDisabled={getOptionDisabled}
isOptionEqualToValue={(option, value) => {
if (value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { render, fireEvent, cleanup, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';

import { SAVE_MESSAGE_TEMPLATE_MUTATION } from 'graphql/mutations/MessageTemplate';
import { TEMPLATE_MOCKS } from 'mocks/Template';
import { filterTemplatesQuery } from 'mocks/Template';
import AddToMessageTemplate from './AddToMessageTemplate';

let resultReturned = false;

const mocks = [
...TEMPLATE_MOCKS,
filterTemplatesQuery('', {}),
{
request: {
query: SAVE_MESSAGE_TEMPLATE_MUTATION,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { render, waitFor, fireEvent, screen } from '@testing-library/react';
import { vi } from 'vitest';

import ChatInput from './ChatInput';
import { TEMPLATE_MOCKS } from 'mocks/Template';
import { createMediaMessageMock, getAttachmentPermissionMock, uploadBlobMock } from 'mocks/Attachment';
import { searchInteractive, searchInteractiveHi } from 'mocks/InteractiveMessage';
import '../VoiceRecorder/VoiceRecorder';
import { LexicalWrapper } from 'common/LexicalWrapper';
import { TEMPLATE_MOCKS } from 'mocks/Template';

const mocks = [
searchInteractive,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { MockedProvider } from '@apollo/client/testing';
import ChatTemplates from './ChatTemplates';
import { TEMPLATE_MOCKS } from '../../../../mocks/Template';
import { render, fireEvent, waitFor } from '@testing-library/react';
import { TEMPLATE_MOCKS } from 'mocks/Template';

const mocks = TEMPLATE_MOCKS;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const TemplateButtons = ({
onClick={() => handleButtonClick(type, value)}
startIcon={icon}
disabled={!isSimulator}
data-testid="templateButton"
>
{title}
</Button>
Expand Down
2 changes: 1 addition & 1 deletion src/containers/Form/FormLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export const FormLayout = ({
}
});
// for template create media for attachment
if (isAttachment && payload.type !== 'TEXT' && payload.type) {
if (isAttachment && payload.type !== 'TEXT' && payload.type && !entityId) {
getMediaId(payload)
.then((data: any) => {
if (data) {
Expand Down
106 changes: 106 additions & 0 deletions src/containers/HSM/HSM.helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { CALL_TO_ACTION, MEDIA_MESSAGE_TYPES, QUICK_REPLY } from 'common/constants';

export interface CallToActionTemplate {
type: string;
title: string;
value: string;
}

export interface QuickReplyTemplate {
value: string;
}

export const mediaOptions = MEDIA_MESSAGE_TYPES.map((option: string) => ({ id: option, label: option })).filter(
({ label }) => label !== 'AUDIO' && label !== 'STICKER'
);

export const removeFirstLineBreak = (text: any) =>
text?.length === 1 ? text.slice(0, 1).replace(/(\r\n|\n|\r)/, '') : text;

/**
* Function to convert buttons to template format
*
* @param templateButtons buttons that need to be converted to gupshup format
* @param templateType depending on template type convert button to gupshup format
*
* @return array result
*/
export const convertButtonsToTemplate = (templateButtons: Array<any>, templateType: string | null) =>
templateButtons.reduce((result: any, temp: any) => {
const { title, value } = temp;
if (templateType === CALL_TO_ACTION && value && title) {
result.push(`[${title}, ${value}]`);
}
if (templateType === QUICK_REPLY && value) {
result.push(`[${value}]`);
}
return result;
}, []);

/**
* As messages and buttons are now separated
* we are combining both message and buttons,
* so that you can see preview in simulator
*
* @param templateType template type
* @param message
* @param buttons
*
* @return object {buttons, template}
*/
export const getTemplateAndButtons = (templateType: string, message: string, buttons: string) => {
const templateButtons = JSON.parse(buttons);
let result: any;
if (templateType === CALL_TO_ACTION) {
result = templateButtons.map((button: any) => {
const { phone_number: phoneNo, url, type, text } = button;
return { type, value: url || phoneNo, title: text };
});
}

if (templateType === QUICK_REPLY) {
result = templateButtons.map((button: any) => {
const { text, type } = button;
return { type, value: text };
});
}

// Getting in template format of gupshup
const templateFormat = convertButtonsToTemplate(result, templateType);
// Pre-pending message with buttons
const template = `${message} | ${templateFormat.join(' | ')}`;
return { buttons: result, template };
};

export const getExampleFromBody = (body: string, variables: Array<any>) => {
return body.replace(/{{(\d+)}}/g, (match, number) => {
let index = parseInt(number) - 1;

return variables[index]?.text ? (variables[index] ? `[${variables[index]?.text}]` : match) : `{{${number}}}`;
});
};

export const getVariables = (message: string, variables: any) => {
const regex = /{{\d+}}/g;
const matches = message.match(regex);

if (!matches) {
return [];
}

return matches.map((match, index) => (variables[index]?.text ? variables[index] : { text: '', id: index + 1 }));
};

export const getExampleValue = (example: string) => {
const regex = /\[([^\]]+)\]/g;
let match;
const variables = [];
let id = 1;

while ((match = regex.exec(example)) !== null) {
variables.push({ text: match[1], id });
id++;
}

return variables;
};
102 changes: 102 additions & 0 deletions src/containers/HSM/HSM.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
.Template {
margin: 20px auto;
width: 80%;
text-align: center;
box-shadow: 0 2px 3px #cccccc;
border: 1px solid #eeeeee;
padding: 10px;
box-sizing: border-box;
}

@media (min-width: 600px) {
.Template {
width: 500px;
}
}

.DeleteIcon {
margin-right: 9px !important;
}

.DialogText {
margin-top: 0px;
text-align: center;
color: #073f24;
font-weight: 400;
}

.DeleteButton {
margin-left: auto !important;
}

.Title {
margin-left: 24px !important;
margin-top: 16px !important;
vertical-align: middle;
font-weight: 500 !important;
color: #073f24;
}

.Input {
display: flex;
padding: 8px;
}

.Label {
width: 50%;
align-self: center;
font-weight: bold;
}

.TemplateAdd {
width: fit-content;
}

.Form {
padding: 16px 16px;
width: 470px;
}

.Buttons {
margin-top: 24px;
margin-left: 8px;
display: flex;
justify-content: flex-start;
}

.Icon {
background-color: #eaedec !important;
margin-right: 10px !important;
}

.ButtonsCenter {
justify-content: center !important;
}

.Button {
margin-right: 24px !important;
}

.Warning {
color: #ff0000;
margin-left: -43px;
}

.IsActive {
color: #555555;
font-weight: 400;
line-height: 18px;
font-size: 16px;
}

.TemplateIcon {
width: 29px;
height: 29px;
}

.Checkbox {
color: #555555;
font-weight: 400;
line-height: 18px;
font-size: 16px;
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import { render, waitFor, within, fireEvent, screen } from '@testing-library/react';
import { render, waitFor, within, fireEvent, screen, cleanup } from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import userEvent from '@testing-library/user-event';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { HSM } from './HSM';
import {
TEMPLATE_MOCKS,
getHSMTemplateTypeMedia,
getHSMTemplateTypeText,
} from 'containers/Template/Template.test.helper';
import { HSM_TEMPLATE_MOCKS, getHSMTemplateTypeMedia, getHSMTemplateTypeText } from 'mocks/Template';
import { setNotification } from 'common/notification';

const mocks = TEMPLATE_MOCKS;
const mocks = HSM_TEMPLATE_MOCKS;

vi.mock('common/notification', async (importOriginal) => {
const mod = await importOriginal<typeof import('common/notification')>();
return {
...mod,
setNotification: vi.fn((...args) => {
return args[1];
}),
};
});

beforeEach(() => {
vi.restoreAllMocks();
cleanup();
});

vi.mock('lexical-beautiful-mentions', async (importOriginal) => {
Expand Down Expand Up @@ -80,6 +87,7 @@ describe('Add mode', () => {
</MemoryRouter>
</MockedProvider>
);

const user = userEvent.setup();

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

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

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

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

autocompletes[3].focus();
fireEvent.keyDown(autocompletes[3], { key: 'ArrowDown' });
fireEvent.click(screen.getByText('Messages'), { key: 'Enter' });
fireEvent.change(inputs[1], { target: { value: 'title' } });

fireEvent.click(screen.getByTestId('submitActionButton'));

await waitFor(() => {
expect(setNotification).toHaveBeenCalled();
});
});

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

fireEvent.click(screen.getAllByTestId('delete-variable')[0]);

await waitFor(() => {
expect(screen.queryByPlaceholderText('Define value ')).not.toBeInTheDocument();
});
});

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

fireEvent.click(screen.getByTestId('submitActionButton'));
fireEvent.click(screen.getByTestId('submitActionButton'));

await waitFor(() => {
expect(setNotification).toHaveBeenCalled();
});
});

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

fireEvent.click(screen.getByTestId('submitActionButton'));
fireEvent.click(screen.getByTestId('submitActionButton'));

await waitFor(() => {
expect(setNotification).toHaveBeenCalled();
});
});

test('adding attachments', async () => {
Expand Down
Loading

0 comments on commit 6a4524d

Please sign in to comment.