From a92d24810a53ca0595525873e794f1e509642f77 Mon Sep 17 00:00:00 2001 From: Anthony Pillot Date: Thu, 2 Mar 2023 13:02:00 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20Prefix=20with=20`[TEST]`?= =?UTF-8?q?=20in=20email=20subject=20if=20not=20in=20prod.=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/outputs/emailer/emailer.output.ts | 14 ++++- src/outputs/google/drive.output.ts | 8 +-- src/services/email.service.ts | 4 +- test/outputs/emailer.output.test.ts | 91 +++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 7 deletions(-) create mode 100644 test/outputs/emailer.output.test.ts diff --git a/src/outputs/emailer/emailer.output.ts b/src/outputs/emailer/emailer.output.ts index db99112..ed7acd7 100644 --- a/src/outputs/emailer/emailer.output.ts +++ b/src/outputs/emailer/emailer.output.ts @@ -27,7 +27,9 @@ export class EmailerOutput { }); } - public sendEmail(mailOptions: EmailOptionsInterface) { + public async sendEmail(mailOptions: EmailOptionsInterface) { + mailOptions = await this.checkEnvironment(mailOptions); + return new Promise((resolve, reject) => { this.transporter.sendMail(mailOptions, (error, data) => { return resolve({ @@ -37,4 +39,14 @@ export class EmailerOutput { }); }); } + + /** + * Verify if the application is running in production mode or not, if not, prefix the @param mailOptions.subject with "[TEST]". + */ + private async checkEnvironment(mailOptions: EmailOptionsInterface): Promise { + if (process.env.NODE_ENV !== "production") { + mailOptions.subject = `(TEST) ${mailOptions.subject}`; + } + return mailOptions; + } } diff --git a/src/outputs/google/drive.output.ts b/src/outputs/google/drive.output.ts index 61dc78d..8747f74 100644 --- a/src/outputs/google/drive.output.ts +++ b/src/outputs/google/drive.output.ts @@ -19,7 +19,7 @@ export class DriveOutput { * @returns The content of the file. */ public async getDocument(fileName: string): Promise { - fileName = this.verify(fileName); + fileName = this.checkEnvironment(fileName); const id = await this.getFileId(fileName); if (id) { @@ -36,7 +36,7 @@ export class DriveOutput { } public async updateDocument(fileName: string, content: string): Promise { - fileName = this.verify(fileName); + fileName = this.checkEnvironment(fileName); const id = await this.getFileId(fileName); if (id) { @@ -77,11 +77,11 @@ export class DriveOutput { } /** - * Verify if the application is running in production mode or not, if not, prefix the @param fileName with "prep-filename.json". + * Check if the application is running in production mode or not, if not, prefix the @param fileName with "prep-filename.json". * Because Google Drive API not permit to search file by folder, all prep. files are prefixed by "prep-filename.json" in the Google Drive. * @param fileName The name of the file to retrieve. */ - private verify(fileName: string): string { + private checkEnvironment(fileName: string): string { if (process.env.NODE_ENV !== "production") { fileName = `prep-${fileName}`; return fileName; diff --git a/src/services/email.service.ts b/src/services/email.service.ts index ccca1a4..bab9a50 100644 --- a/src/services/email.service.ts +++ b/src/services/email.service.ts @@ -67,8 +67,8 @@ export class EmailService { const $emailsToSend = emailList.map((element) => { // Create email template datasToCompile.uuid = receivers?.find((el) => (el.email = element))?.uuid; - const emailOptions: EmailOptionsInterface = this.prepareTemplate(subject, datasToCompile); - return this.emailer.sendEmail({ ...emailOptions, to: [element] }); + const mailOptions: EmailOptionsInterface = this.prepareTemplate(subject, datasToCompile); + return this.emailer.sendEmail({ ...mailOptions, to: [element] }); }); // Send all emails diff --git a/test/outputs/emailer.output.test.ts b/test/outputs/emailer.output.test.ts new file mode 100644 index 0000000..37f3645 --- /dev/null +++ b/test/outputs/emailer.output.test.ts @@ -0,0 +1,91 @@ +import { EmailerOutput } from "../../src/outputs/emailer/emailer.output"; + +import nodemailer from "nodemailer"; + +describe("EmailerOutput", () => { + jest.mock("nodemailer"); + nodemailer.createTransport = jest.fn().mockReturnValue({ + sendMail: jest.fn().mockImplementation((mailOptions, callback) => { + callback(null, "any"); + }), + }); + + let emailer: EmailerOutput; + + beforeEach(() => { + emailer = new EmailerOutput({ + host: "smtp.example.com", + port: 587, + secure: false, + auth: { + user: "test@example.com", + pass: "password", + }, + tls: { + rejectUnauthorized: false, + }, + }); + }); + + test(`given NODE_ENV is set to "production" and standard mail options, + when sendEmail is called, + then should send an email with the given options`, async () => { + // given + const mailOptions = { + // eslint-disable-next-line @typescript-eslint/quotes + from: '"Test" ', + to: ["john.doe@example.com"], + subject: "Hello", + text: "Hello world!", + html: "Hello world!", + sender: "Tester", + }; + + const mailOptionsCloned = JSON.parse(JSON.stringify(mailOptions)); + + // when + process.env.NODE_ENV = "production"; + + const spyParameter = jest.spyOn(emailer, "sendEmail"); + await emailer.sendEmail(mailOptions); + + // then + expect(spyParameter.mock.calls.length).toEqual(1); + const firstCallParameter = spyParameter.mock.calls[0][0]; + + expect(firstCallParameter).toBeDefined(); + expect(firstCallParameter).toEqual(mailOptionsCloned); + }); + + test(`given NODE_ENV is set to "development" and standard mail options, + when sendEmail is called, + then should send an email with the given options and subject prefixed by (TEST)`, async () => { + // given + const mailOptions = { + // eslint-disable-next-line @typescript-eslint/quotes + from: '"Test" ', + to: ["john.doe@example.com"], + subject: "Hello", + text: "Hello world!", + html: "Hello world!", + sender: "Tester", + }; + + const mailOptionsCloned = JSON.parse(JSON.stringify(mailOptions)); + + // when + process.env.NODE_ENV = "development"; + + const spyParameter = jest.spyOn(emailer, "sendEmail"); + await emailer.sendEmail(mailOptions); + + // then + expect(spyParameter.mock.calls.length).toEqual(1); + + const firstCallParameter = spyParameter.mock.calls[0][0]; + + expect(firstCallParameter).toBeDefined(); + expect(firstCallParameter).not.toEqual(mailOptionsCloned); + expect(firstCallParameter.subject).toEqual(`(TEST) ${mailOptionsCloned.subject}`); + }); +});