Skip to content

Commit

Permalink
Merge pull request #71 from bcgov/fix/email-delimiters
Browse files Browse the repository at this point in the history
Additional handling for email delimiters in roadmap feature
  • Loading branch information
kyle1morel authored Apr 29, 2024
2 parents 62eac75 + 387781d commit 5d669e3
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 7 deletions.
2 changes: 1 addition & 1 deletion frontend/src/components/note/NoteModal.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { Form } from 'vee-validate';
import { nextTick, ref, watch } from 'vue';
import { ref } from 'vue';
import { mixed, object, string } from 'yup';
import { Calendar, Dropdown, InputText, TextArea } from '@/components/form';
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/components/roadmap/Roadmap.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { roadmapService, userService } from '@/services';
import { useConfigStore, useSubmissionStore } from '@/store';
import { PERMIT_NEEDED, PERMIT_STATUS } from '@/utils/enums';
import { roadmapTemplate } from '@/utils/templates';
import { delimitEmails } from '@/utils/utils';
import type { Ref } from 'vue';
import type { Document } from '@/types';
Expand Down Expand Up @@ -38,7 +39,7 @@ const emailValidator = array()
if (this.isType(value) && value !== null) {
return value;
}
return originalValue ? originalValue.split(/[\s;]+/) : [];
return originalValue ? delimitEmails(originalValue) : [];
})
.of(string().email(({ value }) => `${value} is not a valid email`));
Expand Down Expand Up @@ -69,7 +70,7 @@ const confirmSubmit = (data: any) => {
);
toast.success('Roadmap sent');
} catch (e: any) {
toast.error('Failed to send roadmap', e.response.data);
toast.error('Failed to send roadmap', e?.response?.statusText);
}
}
});
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/services/roadmapService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { appAxios } from './interceptors';

import { parseCSV } from '@/utils/utils';
import { delimitEmails } from '@/utils/utils';

import type { Email } from '@/types';

Expand All @@ -12,13 +12,13 @@ export default {
*/
send(activityId: string, selectedFileIds: Array<string>, emailData: Email) {
if (emailData.to && !Array.isArray(emailData.to)) {
emailData.to = parseCSV(emailData.to, ';');
emailData.to = delimitEmails(emailData.to);
}
if (emailData.cc && !Array.isArray(emailData.cc)) {
emailData.cc = parseCSV(emailData.cc, ';');
emailData.cc = delimitEmails(emailData.cc);
}
if (emailData.bcc && !Array.isArray(emailData.bcc)) {
emailData.bcc = parseCSV(emailData.bcc, ';');
emailData.bcc = delimitEmails(emailData.bcc);
}

return appAxios().put('roadmap', { activityId, selectedFileIds, emailData });
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { DELIMITER, FILE_CATEGORIES } from '@/utils/constants';

/**
* @function delimitEmails
* Converts a space, semi-colon, or comma-separated value string into an array of string values
* @param {string} value The string to parse
* @returns {string[]} An array of string values
*/
export function delimitEmails(value: string): Array<string> {
return value.split(/[\s;,]+/g).map((s) => s.trim());
}

/**
* @function differential
* Create a key/value differential from source against comparer
Expand Down
79 changes: 79 additions & 0 deletions frontend/tests/unit/service/roadmapService.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { roadmapService } from '@/services';
import { appAxios } from '@/services/interceptors';

vi.mock('vue-router', () => ({
useRouter: () => ({
push: vi.fn()
})
}));

const PATH = 'roadmap';
// const getSpy = vi.fn();
const putSpy = vi.fn();

vi.mock('@/services/interceptors');
vi.mocked(appAxios).mockReturnValue({
// get: getSpy,
put: putSpy
} as any);

const testAddressString = ['test.1.address@test.bc.ca', 'testAddress2@test.com', 'test3Email@test.ca'];
const unformattedEmailList = 'test.1.address@test.bc.ca, testAddress2@test.com,; test3Email@test.ca';
const bccField = 'addressTest2@test.com';

const testData = {
activityId: '123-123',
emailData: {
from: 'fromAddress@test.com',
to: testAddressString,
bcc: [bccField],
subject: 'Here is your housing project Permit Roadmap',
bodyType: 'text',
body: 'testText'
}
};

beforeEach(() => {
vi.clearAllMocks();
});

describe('roadmapService test', () => {
it('sends email when called with right params', async () => {
await roadmapService.send(testData.activityId, ['testSelectedFileIds'], testData.emailData);

expect(putSpy).toHaveBeenCalledTimes(1);
expect(putSpy).toHaveBeenCalledWith(PATH, {
activityId: testData.activityId,
selectedFileIds: ['testSelectedFileIds'],
emailData: expect.objectContaining({
to: ['test.1.address@test.bc.ca', 'testAddress2@test.com', 'test3Email@test.ca'],
bcc: [bccField]
})
});
expect(putSpy).not.toHaveBeenCalledWith(expect.objectContaining({ cc: expect.anything() }));
});

it('formats improper to/cc/bcc fields', async () => {
const modifiedEmail = {
...testData.emailData,
to: unformattedEmailList,
cc: unformattedEmailList,
bcc: unformattedEmailList
};

// @ts-expect-error: testing if email object it not proper type
await roadmapService.send(testData.activityId, ['testSelectedFileIds'], modifiedEmail);

expect(putSpy).toHaveBeenCalledTimes(1);
expect(putSpy).toHaveBeenCalledWith(
PATH,
expect.objectContaining({
emailData: expect.objectContaining({
to: ['test.1.address@test.bc.ca', 'testAddress2@test.com', 'test3Email@test.ca'],
cc: ['test.1.address@test.bc.ca', 'testAddress2@test.com', 'test3Email@test.ca'],
bcc: ['test.1.address@test.bc.ca', 'testAddress2@test.com', 'test3Email@test.ca']
})
})
);
});
});

0 comments on commit 5d669e3

Please sign in to comment.