From 4e2584684162e5c3a57e5890244c3d1f8c0f790b Mon Sep 17 00:00:00 2001 From: Jared White Date: Wed, 18 Dec 2024 18:28:12 -0800 Subject: [PATCH 1/4] feat: lottery opportunity email content updates --- .../seed-helpers/translation-factory.ts | 1 + .../controllers/script-runner.controller.ts | 15 ++++++ api/src/services/email.service.ts | 5 +- api/src/services/script-runner.service.ts | 51 ++++++++++++++++++ api/src/views/listing-opportunity.hbs | 3 +- api/test/unit/services/email.service.spec.ts | 53 +++++++++++++++++++ 6 files changed, 125 insertions(+), 3 deletions(-) diff --git a/api/prisma/seed-helpers/translation-factory.ts b/api/prisma/seed-helpers/translation-factory.ts index 4d330f896b..0ff65731f5 100644 --- a/api/prisma/seed-helpers/translation-factory.ts +++ b/api/prisma/seed-helpers/translation-factory.ts @@ -160,6 +160,7 @@ const translations = (jurisdictionName?: string, language?: LanguagesEnum) => { rentalOpportunity: { subject: 'New rental opportunity', intro: 'Rental opportunity at', + viewListingNotice: 'Please view listing for the most updated information', applicationsDue: 'Applications Due', community: 'Community', address: 'Address', diff --git a/api/src/controllers/script-runner.controller.ts b/api/src/controllers/script-runner.controller.ts index bcd3b95ac1..4090ca49bc 100644 --- a/api/src/controllers/script-runner.controller.ts +++ b/api/src/controllers/script-runner.controller.ts @@ -248,4 +248,19 @@ export class ScirptRunnerController { ): Promise { return await this.scriptRunnerService.removeWorkAddresses(req); } + + @Put('addNoticeToListingOpportunityEmail') + @ApiOperation({ + summary: + 'A script that adds notice translations for the listing opportunity email', + operationId: 'addNoticeToListingOpportunityEmail', + }) + @ApiOkResponse({ type: SuccessDTO }) + async addNoticeToListingOpportunityEmail( + @Request() req: ExpressRequest, + ): Promise { + return await this.scriptRunnerService.addNoticeToListingOpportunityEmail( + req, + ); + } } diff --git a/api/src/services/email.service.ts b/api/src/services/email.service.ts index efddcbd0b3..b03cbad35e 100644 --- a/api/src/services/email.service.ts +++ b/api/src/services/email.service.ts @@ -528,7 +528,8 @@ export class EmailService { label: this.polyglot.t('rentalOpportunity.applicationsDue'), value: dayjs(listing.applicationDueDate) .tz(process.env.TIME_ZONE) - .format('MMMM D, YYYY'), + .format('MMMM D, YYYY [at] h:mma PST'), + bolded: true, }); } tableRows.push({ @@ -577,7 +578,7 @@ export class EmailService { label: this.polyglot.t('rentalOpportunity.lottery'), value: dayjs(lotteryEvent.startDate) .tz(process.env.TIME_ZONE) - .format('MMMM D, YYYY'), + .format('MMMM D, YYYY [at] h:mma PST'), }); } } diff --git a/api/src/services/script-runner.service.ts b/api/src/services/script-runner.service.ts index 120252fe8b..595153bcba 100644 --- a/api/src/services/script-runner.service.ts +++ b/api/src/services/script-runner.service.ts @@ -1379,6 +1379,57 @@ export class ScriptRunnerService { return { success: true }; } + /** + * + * @param req incoming request object + * @returns successDTO + * @description updates single use code translations to show extended expiration time + */ + async addNoticeToListingOpportunityEmail( + req: ExpressRequest, + ): Promise { + const requestingUser = mapTo(User, req['user']); + await this.markScriptAsRunStart( + 'add notice translation for listing opportunity email', + requestingUser, + ); + + await this.updateTranslationsForLanguage(LanguagesEnum.en, { + rentalOpportunity: { + viewListingNotice: + 'Please view listing for the most updated information', + }, + }); + await this.updateTranslationsForLanguage(LanguagesEnum.es, { + rentalOpportunity: { + viewListingNotice: + 'Consulte el listado para obtener la información más actualizada', + }, + }); + await this.updateTranslationsForLanguage(LanguagesEnum.tl, { + rentalOpportunity: { + viewListingNotice: + 'Mangyaring tingnan ang listahan para sa pinakabagong impormasyon', + }, + }); + await this.updateTranslationsForLanguage(LanguagesEnum.vi, { + rentalOpportunity: { + viewListingNotice: 'Vui lòng xem danh sách để biết thông tin mới nhất', + }, + }); + await this.updateTranslationsForLanguage(LanguagesEnum.zh, { + rentalOpportunity: { + viewListingNotice: '请查看列表以获取最新信息', + }, + }); + + await this.markScriptAsComplete( + 'add notice translation for listing opportunity email', + requestingUser, + ); + return { success: true }; + } + /** this is simply an example */ diff --git a/api/src/views/listing-opportunity.hbs b/api/src/views/listing-opportunity.hbs index 923ead50cf..51480e8b74 100644 --- a/api/src/views/listing-opportunity.hbs +++ b/api/src/views/listing-opportunity.hbs @@ -1,6 +1,7 @@ {{#> layout_default }}

{{t "rentalOpportunity.intro"}}
{{{listingName}}}

+

{{t "rentalOpportunity.viewListingNotice"}}

@@ -12,7 +13,7 @@ - diff --git a/api/test/unit/services/email.service.spec.ts b/api/test/unit/services/email.service.spec.ts index 07ea3cef24..5ca5894b96 100644 --- a/api/test/unit/services/email.service.spec.ts +++ b/api/test/unit/services/email.service.spec.ts @@ -2,6 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ConfigModule } from '@nestjs/config'; import { LanguagesEnum, + ListingEventsTypeEnum, ListingsStatusEnum, ReviewOrderTypeEnum, } from '@prisma/client'; @@ -16,8 +17,10 @@ import { User } from '../../../src/dtos/users/user.dto'; import { ApplicationCreate } from '../../../src/dtos/applications/application-create.dto'; import { of } from 'rxjs'; import { HttpService } from '@nestjs/axios'; +import dayjs from 'dayjs'; let sendMock; +let govSendMock; const translationServiceMock = { getMergedTranslations: () => { return translationFactory().translations; @@ -64,8 +67,10 @@ describe('Testing email service', () => { beforeEach(async () => { jest.useFakeTimers(); sendMock = jest.fn(); + govSendMock = jest.fn(); service = await module.resolve(EmailService); service.sendSES = sendMock; + service.govSend = govSendMock; }); const user = { @@ -443,5 +448,53 @@ describe('Testing email service', () => { expect(emailMock.html).toMatch('Thank you,'); expect(emailMock.html).toMatch('Bloom Housing Portal'); }); + + it('should notify people of listing opportunity', async () => { + const listing = { + id: 'listingId', + name: 'test listing', + reviewOrderType: ReviewOrderTypeEnum.firstComeFirstServe, + applicationDueDate: new Date(), + status: ListingsStatusEnum.active, + jurisdictions: { name: 'test jurisdiction', id: 'jurisdictionId' }, + displayWaitlistSize: false, + showWaitlist: false, + applicationMethods: [], + assets: [], + listingEvents: [ + { + type: ListingEventsTypeEnum.publicLottery, + startDate: new Date(), + }, + ], + units: [], + referralApplication: undefined, + createdAt: new Date(), + updatedAt: new Date(), + listingsBuildingAddress: { + ...yellowstoneAddress, + id: 'addressId', + createdAt: new Date(), + updatedAt: new Date(), + }, + }; + const service = await module.resolve(EmailService); + await service.listingOpportunity(listing); + + const emailMock = govSendMock.mock.calls[0][0]; + expect(emailMock).toMatch( + 'Rental opportunity at
test listing', + ); + expect(emailMock).toMatch('3200 Old Faithful Inn Rd'); + expect(emailMock).toMatch('Applications Due'); + expect(emailMock).toMatch( + dayjs(listing.applicationDueDate) + .tz(process.env.TIME_ZONE) + .format('MMMM D, YYYY [at] h:mma PST'), + ); + expect(emailMock).toMatch( + 'Please view listing for the most updated information', + ); + }); }); }); From 3e3c9dfe56f5d82868541a2a2fa94f5db7f8de4a Mon Sep 17 00:00:00 2001 From: Jared White Date: Fri, 20 Dec 2024 16:34:44 -0800 Subject: [PATCH 2/4] test: add more robust email testing and also test script run --- api/src/views/listing-opportunity.hbs | 2 +- api/test/unit/services/email.service.spec.ts | 21 ++++++---- .../services/script-runner.service.spec.ts | 42 +++++++++++++++++++ 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/api/src/views/listing-opportunity.hbs b/api/src/views/listing-opportunity.hbs index 51480e8b74..5f34b99b65 100644 --- a/api/src/views/listing-opportunity.hbs +++ b/api/src/views/listing-opportunity.hbs @@ -1,6 +1,6 @@ {{#> layout_default }} -

{{t "rentalOpportunity.intro"}}
{{{listingName}}}

+

{{t "rentalOpportunity.intro"}}
{{{listingName}}}

{{t "rentalOpportunity.viewListingNotice"}}

diff --git a/api/test/unit/services/email.service.spec.ts b/api/test/unit/services/email.service.spec.ts index 5ca5894b96..8dbecee823 100644 --- a/api/test/unit/services/email.service.spec.ts +++ b/api/test/unit/services/email.service.spec.ts @@ -483,17 +483,24 @@ describe('Testing email service', () => { const emailMock = govSendMock.mock.calls[0][0]; expect(emailMock).toMatch( - 'Rental opportunity at
test listing', + 'Rental opportunity at
test listing', ); - expect(emailMock).toMatch('3200 Old Faithful Inn Rd'); - expect(emailMock).toMatch('Applications Due'); expect(emailMock).toMatch( - dayjs(listing.applicationDueDate) - .tz(process.env.TIME_ZONE) - .format('MMMM D, YYYY [at] h:mma PST'), + 'Please view listing for the most updated information', ); expect(emailMock).toMatch( - 'Please view listing for the most updated information', + /`, + ), + ); + expect(emailMock).toMatch(/`, + .format('MMMM D, YYYY [at] h:mma z')}\\s*`, ), ); expect(emailMock).toMatch(/