From 396f31f3500694fdd9c009537141e5ef100d773d Mon Sep 17 00:00:00 2001 From: Kyle Hoehns <9507268+kylehoehns@users.noreply.github.com> Date: Wed, 10 Dec 2025 11:25:35 -0600 Subject: [PATCH] add candidate type and include test duration info in message to reviewers --- src/bot/__tests__/acceptReviewRequest.test.ts | 11 ++++- src/bot/__tests__/getReviewInfo.test.ts | 3 +- src/bot/__tests__/requestReview.test.ts | 26 +++++++++-- src/bot/acceptReviewRequest.ts | 5 ++- src/bot/enums.ts | 11 +++++ src/bot/requestReview.ts | 44 ++++++++++++++++++- src/cron/__tests__/reviewProcessor.test.ts | 3 +- src/database/models/ActiveReview.ts | 6 ++- src/database/repos/activeReviewsRepo.ts | 3 ++ src/services/ChatService.ts | 2 + src/services/RequestService.ts | 4 +- src/services/__tests__/ChatService.test.ts | 15 ++++++- src/services/__tests__/QueueService.test.ts | 3 +- src/services/__tests__/RequestService.test.ts | 7 ++- .../__tests__/ReviewActionService.test.ts | 3 +- src/services/__tests__/reviewCloser.test.ts | 6 ++- src/utils/RequestBuilder.ts | 18 +++++++- 17 files changed, 152 insertions(+), 18 deletions(-) diff --git a/src/bot/__tests__/acceptReviewRequest.test.ts b/src/bot/__tests__/acceptReviewRequest.test.ts index 73b233c2..f04a8950 100644 --- a/src/bot/__tests__/acceptReviewRequest.test.ts +++ b/src/bot/__tests__/acceptReviewRequest.test.ts @@ -1,6 +1,6 @@ import { acceptReviewRequest } from '@bot/acceptReviewRequest'; import { buildMockActionParam, buildMockApp } from '@utils/slackMocks'; -import { BlockId } from '@bot/enums'; +import { BlockId, CandidateType } from '@bot/enums'; import { chatService } from '@/services/ChatService'; import { userRepo } from '@repos/userRepo'; import { addUserToAcceptedReviewers } from '@/services/RequestService'; @@ -20,6 +20,13 @@ describe('acceptReviewRequest', () => { activeReviewRepo.getReviewByThreadIdOrUndefined = jest.fn(); }); + const expectedCandidateTypeBlock = { + type: 'section', + text: { + type: 'mrkdwn', + text: '*Candidate Type:* Full-time', + }, + }; const expectedHackerRankUrlBlock = { type: 'section', text: { @@ -71,6 +78,7 @@ describe('acceptReviewRequest', () => { (activeReviewRepo.getReviewByThreadIdOrUndefined as jest.Mock).mockResolvedValue({ hackerRankUrl: 'https://www.sourceallies.com', requestorId: 'requester123', + candidateType: CandidateType.FULL_TIME, acceptedReviewers: [], declinedReviewers: [], pendingReviewers: [{ userId: action.body.user.id }], @@ -126,6 +134,7 @@ describe('acceptReviewRequest', () => { ); expectUpdatedWithBlocks( action, + expectedCandidateTypeBlock, expectedHackerRankUrlBlock, expectedHackerRankInstructionsBlock, expectedHackerRankAccountHelpBlock, diff --git a/src/bot/__tests__/getReviewInfo.test.ts b/src/bot/__tests__/getReviewInfo.test.ts index b7e7f474..41f118a1 100644 --- a/src/bot/__tests__/getReviewInfo.test.ts +++ b/src/bot/__tests__/getReviewInfo.test.ts @@ -1,7 +1,7 @@ import { App } from '@slack/bolt'; import { getReviewInfo } from '@bot/getReviewInfo'; import { buildMockGlobalShortcutParam, buildMockWebClient } from '@utils/slackMocks'; -import { Deadline, Interaction } from '@bot/enums'; +import { CandidateType, Deadline, Interaction } from '@bot/enums'; import { GlobalShortcutParam } from '@/slackTypes'; import { activeReviewRepo } from '@repos/activeReviewsRepo'; import { ActiveReview } from '@models/ActiveReview'; @@ -47,6 +47,7 @@ describe('getReviewInfo', () => { requestedAt: new Date(1650504468906), dueBy: Deadline.END_OF_DAY, candidateIdentifier: 'some-id', + candidateType: CandidateType.FULL_TIME, reviewersNeededCount: 1, acceptedReviewers: [], declinedReviewers: [], diff --git a/src/bot/__tests__/requestReview.test.ts b/src/bot/__tests__/requestReview.test.ts index 81679cb7..8e627df3 100644 --- a/src/bot/__tests__/requestReview.test.ts +++ b/src/bot/__tests__/requestReview.test.ts @@ -2,7 +2,7 @@ import { activeReviewRepo } from '@/database/repos/activeReviewsRepo'; import { QueueService } from '@/services'; import { chatService } from '@/services/ChatService'; import { ShortcutParam } from '@/slackTypes'; -import { ActionId, Deadline, Interaction } from '@bot/enums'; +import { ActionId, CandidateType, Deadline, Interaction } from '@bot/enums'; import { requestReview } from '@bot/requestReview'; import { languageRepo } from '@repos/languageRepo'; import { App, SlackViewAction, ViewStateValue } from '@slack/bolt'; @@ -157,10 +157,17 @@ describe('requestReview', () => { expect(blocks[2].element.initial_value).toEqual('2'); }); - it('should setup the fifth response block for the HackerRank URL input', () => { + it('should setup the fifth response block for the candidate type dropdown', () => { const { mock } = param.client.views.open as jest.Mock; const blocks = mock.calls[0][0].view.blocks; - expect(blocks[4]).toEqual({ + expect(blocks[4].block_id).toEqual(ActionId.CANDIDATE_TYPE); + expect(blocks[4].type).toEqual('input'); + }); + + it('should setup the sixth response block for the HackerRank URL input', () => { + const { mock } = param.client.views.open as jest.Mock; + const blocks = mock.calls[0][0].view.blocks; + expect(blocks[5]).toEqual({ type: 'input', block_id: ActionId.HACKERRANK_URL, label: { @@ -265,6 +272,15 @@ describe('requestReview', () => { value: 'some-identifier', }, }, + [ActionId.CANDIDATE_TYPE]: { + [ActionId.CANDIDATE_TYPE]: { + type: 'static_select', + selected_option: { + text: { type: 'plain_text', text: 'Full-time' }, + value: CandidateType.FULL_TIME, + }, + }, + }, [ActionId.HACKERRANK_URL]: { [ActionId.HACKERRANK_URL]: { type: 'plain_text_input', @@ -331,10 +347,13 @@ describe('requestReview', () => { • Go • Javascript +*Candidate Type: Full-time* + *The review is needed by end of day Monday* _Candidate Identifier: some-identifier_ `.trim(), + token: undefined, }); }); @@ -354,6 +373,7 @@ _Candidate Identifier: some-identifier_ requestedAt: expect.any(Date), dueBy: Deadline.MONDAY, candidateIdentifier: 'some-identifier', + candidateType: CandidateType.FULL_TIME, reviewersNeededCount: '1', acceptedReviewers: [], declinedReviewers: [], diff --git a/src/bot/acceptReviewRequest.ts b/src/bot/acceptReviewRequest.ts index 2a94afa9..980a510f 100644 --- a/src/bot/acceptReviewRequest.ts +++ b/src/bot/acceptReviewRequest.ts @@ -1,7 +1,7 @@ import { ActionParam } from '@/slackTypes'; import { App } from '@slack/bolt'; import log from '@utils/log'; -import { ActionId, BlockId } from './enums'; +import { ActionId, BlockId, CandidateTypeLabel } from './enums'; import { userRepo } from '@repos/userRepo'; import { mention, textBlock } from '@utils/text'; import { reportErrorAndContinue } from '@utils/reportError'; @@ -86,6 +86,9 @@ export const acceptReviewRequest = { // Add HackerRank URL with instructions if available const review = await activeReviewRepo.getReviewByThreadIdOrUndefined(threadId); if (review) { + blocks.push( + textBlock(`*Candidate Type:* ${CandidateTypeLabel.get(review.candidateType)}`), + ); blocks.push( textBlock(`*HackerRank Report:* <${review.hackerRankUrl}|View Candidate Assessment>`), ); diff --git a/src/bot/enums.ts b/src/bot/enums.ts index 9da2d07d..7aa9ccbf 100644 --- a/src/bot/enums.ts +++ b/src/bot/enums.ts @@ -19,6 +19,7 @@ export const enum ActionId { REVIEW_DEADLINE = 'review-deadline', NUMBER_OF_REVIEWERS = 'number-of-reviewers', CANDIDATE_IDENTIFIER = 'candidate-identifier', + CANDIDATE_TYPE = 'candidate-type', REVIEWER_DM_ACCEPT = 'reviewer-dm-accept', REVIEWER_DM_DECLINE = 'reviewer-dm-deny', HACKERRANK_URL = 'hackerrank-url', @@ -46,3 +47,13 @@ export const DeadlineLabel = new Map([ [Deadline.THURSDAY, 'Thursday'], [Deadline.FRIDAY, 'Friday'], ]); + +export const enum CandidateType { + FULL_TIME = 'full-time', + APPRENTICE = 'apprentice', +} + +export const CandidateTypeLabel = new Map([ + [CandidateType.FULL_TIME, 'Full-time'], + [CandidateType.APPRENTICE, 'Apprentice'], +]); diff --git a/src/bot/requestReview.ts b/src/bot/requestReview.ts index 0944013d..9508b803 100644 --- a/src/bot/requestReview.ts +++ b/src/bot/requestReview.ts @@ -9,7 +9,14 @@ import { blockUtils } from '@utils/blocks'; import log from '@utils/log'; import { bold, codeBlock, compose, italic, mention, ul } from '@utils/text'; import { PendingReviewer } from '@models/ActiveReview'; -import { ActionId, Deadline, DeadlineLabel, Interaction } from './enums'; +import { + ActionId, + CandidateType, + CandidateTypeLabel, + Deadline, + DeadlineLabel, + Interaction, +} from './enums'; import { chatService } from '@/services/ChatService'; import { determineExpirationTime } from '@utils/reviewExpirationUtils'; @@ -89,6 +96,19 @@ export const requestReview = { }, }, }, + { + type: 'input', + block_id: ActionId.CANDIDATE_TYPE, + label: { + text: 'What type of candidate is this?', + type: 'plain_text', + }, + element: { + type: 'static_select', + action_id: ActionId.CANDIDATE_TYPE, + options: buildCandidateTypeOptions(), + }, + }, { type: 'input', block_id: ActionId.HACKERRANK_URL, @@ -160,12 +180,15 @@ export const requestReview = { const deadline = blockUtils.getBlockValue(body, ActionId.REVIEW_DEADLINE); const numberOfRequestedReviewers = blockUtils.getBlockValue(body, ActionId.NUMBER_OF_REVIEWERS); const candidateIdentifier = blockUtils.getBlockValue(body, ActionId.CANDIDATE_IDENTIFIER); + const candidateType = blockUtils.getBlockValue(body, ActionId.CANDIDATE_TYPE); const hackerRankUrl = blockUtils.getBlockValue(body, ActionId.HACKERRANK_URL); const numberOfReviewersValue = numberOfRequestedReviewers.value; const deadlineValue = deadline.selected_option.value; const deadlineDisplay = deadline.selected_option.text.text; const candidateIdentifierValue = candidateIdentifier.value; + const candidateTypeValue = candidateType.selected_option.value; + const candidateTypeDisplay = candidateType.selected_option.text.text; const hackerRankUrlValue = hackerRankUrl.value; log.d( 'requestReview.callback', @@ -173,6 +196,8 @@ export const requestReview = { JSON.stringify({ numberOfReviewersValue, candidateIdentifierValue, + candidateTypeValue, + candidateTypeDisplay, hackerRankUrlValue, deadlineValue, deadlineDisplay, @@ -190,6 +215,7 @@ export const requestReview = { user, )} has requested ${numberOfReviewersValue} reviews for a HackerRank done in the following languages:`, ul(...languages), + bold(`Candidate Type: ${candidateTypeDisplay}`), bold(`The review is needed by end of day ${deadlineDisplay}`), candidateIdentifierValue ? italic(`Candidate Identifier: ${candidateIdentifierValue}`) : '', ), @@ -226,6 +252,7 @@ export const requestReview = { { id: user.id }, languages, deadlineDisplay, + candidateTypeDisplay, ); const pendingReviewer: PendingReviewer = { userId: reviewer.id, @@ -242,6 +269,7 @@ export const requestReview = { requestedAt: new Date(), dueBy: deadlineValue, candidateIdentifier: candidateIdentifierValue, + candidateType: candidateTypeValue, reviewersNeededCount: numberOfReviewersValue, acceptedReviewers: [], declinedReviewers: [], @@ -265,3 +293,17 @@ function buildDeadlineOptions(): PlainTextOption[] { function buildOption(deadline: Deadline): PlainTextOption { return { text: { text: DeadlineLabel.get(deadline) || '', type: 'plain_text' }, value: deadline }; } + +function buildCandidateTypeOptions(): PlainTextOption[] { + return [ + buildCandidateTypeOption(CandidateType.FULL_TIME), + buildCandidateTypeOption(CandidateType.APPRENTICE), + ]; +} + +function buildCandidateTypeOption(candidateType: CandidateType): PlainTextOption { + return { + text: { text: CandidateTypeLabel.get(candidateType) || '', type: 'plain_text' }, + value: candidateType, + }; +} diff --git a/src/cron/__tests__/reviewProcessor.test.ts b/src/cron/__tests__/reviewProcessor.test.ts index 6de0aad6..9e63bd4b 100644 --- a/src/cron/__tests__/reviewProcessor.test.ts +++ b/src/cron/__tests__/reviewProcessor.test.ts @@ -1,4 +1,4 @@ -import { Deadline } from '@/bot/enums'; +import { CandidateType, Deadline } from '@/bot/enums'; import { ActiveReview, PendingReviewer } from '@/database/models/ActiveReview'; import { activeReviewRepo } from '@/database/repos/activeReviewsRepo'; import { RequestService } from '@/services'; @@ -15,6 +15,7 @@ function mockReview(pendingReviewers: PendingReviewer[]): ActiveReview { acceptedReviewers: [], dueBy: Deadline.MONDAY, candidateIdentifier: '', + candidateType: CandidateType.FULL_TIME, languages: [], pendingReviewers, declinedReviewers: [], diff --git a/src/database/models/ActiveReview.ts b/src/database/models/ActiveReview.ts index b5e342e6..ac029f96 100644 --- a/src/database/models/ActiveReview.ts +++ b/src/database/models/ActiveReview.ts @@ -1,4 +1,4 @@ -import { Deadline } from '@bot/enums'; +import { CandidateType, Deadline } from '@bot/enums'; export interface ActiveReview { threadId: string; @@ -7,6 +7,10 @@ export interface ActiveReview { requestedAt: Date; dueBy: Deadline; candidateIdentifier: string; + /** + * The type of candidate (full-time or apprentice) + */ + candidateType: CandidateType; /** * The number of reviewers requested for this review. It should not change over the life of the * review diff --git a/src/database/repos/activeReviewsRepo.ts b/src/database/repos/activeReviewsRepo.ts index 4e4c806c..10eee527 100644 --- a/src/database/repos/activeReviewsRepo.ts +++ b/src/database/repos/activeReviewsRepo.ts @@ -11,6 +11,7 @@ enum Column { REQUESTED_AT = 'requestedAt', DUE_BY = 'dueBy', CANDIDATE_IDENTIFIER = 'candidateIdentifier', + CANDIDATE_TYPE = 'candidateType', REVIEWERS_NEEDED_COUNT = 'reviewersNeededCount', ACCEPTED_REVIEWERS = 'acceptedReviewers', PENDING_REVIEWERS = 'pendingReviewers', @@ -34,6 +35,7 @@ function mapRowToActiveReview(row: GoogleSpreadsheetRow): ActiveReview { requestedAt: parseDateRow(row.get(Column.REQUESTED_AT)), dueBy: row.get(Column.DUE_BY), candidateIdentifier: row.get(Column.CANDIDATE_IDENTIFIER), + candidateType: row.get(Column.CANDIDATE_TYPE), reviewersNeededCount: Number(row.get(Column.REVIEWERS_NEEDED_COUNT)), acceptedReviewers: JSON.parse(row.get(Column.ACCEPTED_REVIEWERS)), pendingReviewers: JSON.parse(row.get(Column.PENDING_REVIEWERS)), @@ -50,6 +52,7 @@ function mapActiveReviewToRow(activeReview: ActiveReview): Record { [Column.REQUESTED_AT]: activeReview.requestedAt.getTime(), [Column.DUE_BY]: activeReview.dueBy, [Column.CANDIDATE_IDENTIFIER]: activeReview.candidateIdentifier, + [Column.CANDIDATE_TYPE]: activeReview.candidateType, [Column.REVIEWERS_NEEDED_COUNT]: activeReview.reviewersNeededCount, [Column.ACCEPTED_REVIEWERS]: JSON.stringify(activeReview.acceptedReviewers), [Column.PENDING_REVIEWERS]: JSON.stringify(activeReview.pendingReviewers), diff --git a/src/services/ChatService.ts b/src/services/ChatService.ts index fc2d725e..dffba2c9 100644 --- a/src/services/ChatService.ts +++ b/src/services/ChatService.ts @@ -89,6 +89,7 @@ export const chatService = { requestor: { id: string }, languages: string[], deadlineDisplay: string, + candidateTypeDisplay: string, ): Promise { const request = requestBuilder.buildReviewRequest( reviewerId, @@ -96,6 +97,7 @@ export const chatService = { requestor, languages, deadlineDisplay, + candidateTypeDisplay, ); const requestWithToken = { ...request, diff --git a/src/services/RequestService.ts b/src/services/RequestService.ts index cb6ff0dc..2b1d6116 100644 --- a/src/services/RequestService.ts +++ b/src/services/RequestService.ts @@ -3,7 +3,7 @@ import { activeReviewRepo } from '@/database/repos/activeReviewsRepo'; import { WebClient } from '@/slackTypes'; import { QueueService } from '@services'; import { chatService } from '@/services/ChatService'; -import { DeadlineLabel } from '@bot/enums'; +import { CandidateTypeLabel, DeadlineLabel } from '@bot/enums'; import { requestBuilder } from '@utils/RequestBuilder'; import { textBlock } from '@utils/text'; import { App } from '@slack/bolt'; @@ -64,6 +64,7 @@ const closeRequestInternal = async ( { id: updatedReview.requestorId }, updatedReview.languages, DeadlineLabel.get(updatedReview.dueBy) || 'Unknown', + CandidateTypeLabel.get(updatedReview.candidateType) || 'Unknown', ); const closeMessageBlock = textBlock(closeMessage); await chatService.updateDirectMessage( @@ -107,6 +108,7 @@ async function requestNextUserReview(review: ActiveReview, _client: WebClient): { id: review.requestorId }, review.languages, DeadlineLabel.get(review.dueBy) || '', + CandidateTypeLabel.get(review.candidateType) || 'Unknown', ); const pendingReviewer: PendingReviewer = { ...nextUser, diff --git a/src/services/__tests__/ChatService.test.ts b/src/services/__tests__/ChatService.test.ts index dc66fab7..d6c919b2 100644 --- a/src/services/__tests__/ChatService.test.ts +++ b/src/services/__tests__/ChatService.test.ts @@ -1,7 +1,7 @@ import { buildMockWebClient } from '@utils/slackMocks'; import { chatService } from '@/services/ChatService'; import { Block } from '@slack/types'; -import { Deadline, DeadlineLabel } from '@bot/enums'; +import { CandidateType, CandidateTypeLabel, Deadline, DeadlineLabel } from '@bot/enums'; describe('ChatService', () => { const OLD_ENV = process.env; @@ -67,13 +67,23 @@ describe('ChatService', () => { const threadId = '456'; const requestorId = '789'; const languages = ['Java', 'Python']; + const candidateTypeDisplay = CandidateTypeLabel.get(CandidateType.FULL_TIME) || ''; // prettier-ignore const requestBlock = `<@${requestorId}> has requested a HackerRank review done in the following languages: • ${languages[0]} • ${languages[1]} -*The review is needed by end of day Today*`; +*Candidate Type: ${candidateTypeDisplay}* + +*The review is needed by end of day Today* + +*Test Information:* + +The test has 4 questions: 2 easy and 2 medium difficulty. +Section 1 contains the easy questions, Section 2 contains the medium questions. +Candidates should try to solve one problem from each section. +They have 70 minutes total to complete the test.`; await chatService.sendRequestReviewMessage( client, reviewerId, @@ -81,6 +91,7 @@ describe('ChatService', () => { { id: requestorId }, languages, DeadlineLabel.get(Deadline.END_OF_DAY) || '', + candidateTypeDisplay, ); expect(client.chat.postMessage).toHaveBeenCalledWith({ diff --git a/src/services/__tests__/QueueService.test.ts b/src/services/__tests__/QueueService.test.ts index 364d2640..315f29aa 100644 --- a/src/services/__tests__/QueueService.test.ts +++ b/src/services/__tests__/QueueService.test.ts @@ -3,7 +3,7 @@ import { userRepo } from '@/database/repos/userRepo'; import Time from '@utils/time'; import { byLastReviewedDate, getInitialUsersForReview } from '../QueueService'; import { ActiveReview } from '@models/ActiveReview'; -import { Deadline } from '@bot/enums'; +import { CandidateType, Deadline } from '@bot/enums'; import { activeReviewRepo } from '@repos/activeReviewsRepo'; function makeUser(timeSinceLastReview: number | null): User { @@ -150,6 +150,7 @@ describe('Queue Service', () => { requestedAt: new Date(), dueBy: Deadline.END_OF_DAY, candidateIdentifier: '123', + candidateType: CandidateType.FULL_TIME, reviewersNeededCount: 2, acceptedReviewers: [], declinedReviewers: [], diff --git a/src/services/__tests__/RequestService.test.ts b/src/services/__tests__/RequestService.test.ts index a450b967..ab864208 100644 --- a/src/services/__tests__/RequestService.test.ts +++ b/src/services/__tests__/RequestService.test.ts @@ -5,7 +5,7 @@ import { PartialPendingReviewer, } from '@/database/models/ActiveReview'; import { activeReviewRepo } from '@/database/repos/activeReviewsRepo'; -import { Deadline } from '@bot/enums'; +import { CandidateType, Deadline } from '@bot/enums'; import { RequestService, QueueService } from '@/services'; import { chatService } from '@/services/ChatService'; import { expireRequest } from '@/services/RequestService'; @@ -24,6 +24,7 @@ describe('RequestService', () => { requestedAt: new Date(), dueBy: Deadline.END_OF_DAY, candidateIdentifier: 'some-id', + candidateType: CandidateType.FULL_TIME, reviewersNeededCount: 2, acceptedReviewers: [acceptedUser('999')], declinedReviewers: [declinedUser('111'), declinedUser('222')], @@ -48,6 +49,7 @@ describe('RequestService', () => { requestedAt: requestedDate, dueBy: Deadline.END_OF_DAY, candidateIdentifier: 'some-id', + candidateType: CandidateType.FULL_TIME, reviewersNeededCount: 2, acceptedReviewers: [acceptedUser('999')], declinedReviewers: [declinedUser('111'), declinedUser('222')], @@ -69,6 +71,7 @@ describe('RequestService', () => { requestedAt: requestedDate, dueBy: Deadline.END_OF_DAY, candidateIdentifier: 'some-id', + candidateType: CandidateType.FULL_TIME, reviewersNeededCount: 2, acceptedReviewers: [ { userId: '999', acceptedAt: expect.any(Number) }, @@ -97,6 +100,7 @@ describe('RequestService', () => { requestedAt: new Date(), dueBy: Deadline.END_OF_DAY, candidateIdentifier: '', + candidateType: CandidateType.FULL_TIME, reviewersNeededCount: 2, acceptedReviewers: [], declinedReviewers: [], @@ -144,6 +148,7 @@ describe('RequestService', () => { { id: review.requestorId }, review.languages, 'Today', + 'Full-time', ); expect(reviewCloser.closeReviewIfComplete).toHaveBeenCalledWith(app, threadId); }); diff --git a/src/services/__tests__/ReviewActionService.test.ts b/src/services/__tests__/ReviewActionService.test.ts index 3931c210..1d2bdaaf 100644 --- a/src/services/__tests__/ReviewActionService.test.ts +++ b/src/services/__tests__/ReviewActionService.test.ts @@ -1,5 +1,5 @@ import { ActiveReview } from '@models/ActiveReview'; -import { Deadline } from '@bot/enums'; +import { CandidateType, Deadline } from '@bot/enums'; import { User } from '@models/User'; import { reviewActionService } from '@/services/ReviewActionService'; import { @@ -19,6 +19,7 @@ describe('ReviewActionService', () => { requestedAt: new Date(1577858300000), dueBy: Deadline.END_OF_DAY, candidateIdentifier: 'some-id', + candidateType: CandidateType.FULL_TIME, reviewersNeededCount: 2, acceptedReviewers: [{ userId: 'A', acceptedAt: 1609480800000 }], declinedReviewers: [{ userId: 'B', declinedAt: 1577858400000 }], diff --git a/src/services/__tests__/reviewCloser.test.ts b/src/services/__tests__/reviewCloser.test.ts index d297ad6a..0a9be7fe 100644 --- a/src/services/__tests__/reviewCloser.test.ts +++ b/src/services/__tests__/reviewCloser.test.ts @@ -1,7 +1,7 @@ import { activeReviewRepo } from '@repos/activeReviewsRepo'; import { chatService } from '@/services/ChatService'; import { AcceptedReviewer, ActiveReview, DeclinedReviewer } from '@models/ActiveReview'; -import { Deadline } from '@bot/enums'; +import { CandidateType, Deadline } from '@bot/enums'; import { App } from '@slack/bolt'; import { buildMockApp } from '@utils/slackMocks'; import { reviewCloser } from '@/services/ReviewCloser'; @@ -32,6 +32,7 @@ describe('reviewCloser', () => { requestedAt: new Date(), dueBy: Deadline.MONDAY, candidateIdentifier: 'some-id', + candidateType: CandidateType.FULL_TIME, reviewersNeededCount: 2, acceptedReviewers: [acceptedUser('A'), acceptedUser('B')], declinedReviewers: [], @@ -60,6 +61,7 @@ describe('reviewCloser', () => { requestedAt: new Date(), dueBy: Deadline.MONDAY, candidateIdentifier: 'some-id', + candidateType: CandidateType.FULL_TIME, reviewersNeededCount: 2, acceptedReviewers: [acceptedUser('B')], declinedReviewers: [ @@ -92,6 +94,7 @@ describe('reviewCloser', () => { requestedAt: new Date(), dueBy: Deadline.MONDAY, candidateIdentifier: 'some-id', + candidateType: CandidateType.FULL_TIME, reviewersNeededCount: 2, acceptedReviewers: [acceptedUser('A')], declinedReviewers: [], @@ -120,6 +123,7 @@ describe('reviewCloser', () => { requestedAt: new Date(), dueBy: Deadline.MONDAY, candidateIdentifier: 'some-id', + candidateType: CandidateType.FULL_TIME, reviewersNeededCount: 2, acceptedReviewers: [acceptedUser('A'), acceptedUser('B')], declinedReviewers: [], diff --git a/src/utils/RequestBuilder.ts b/src/utils/RequestBuilder.ts index 6393777b..9e8d3a06 100644 --- a/src/utils/RequestBuilder.ts +++ b/src/utils/RequestBuilder.ts @@ -10,11 +10,18 @@ export const requestBuilder = { requestor: { id: string }, languages: string[], deadlineDisplay: string, + candidateTypeDisplay: string, ): any { return { channel: reviewerId, text: `HackerRank review requested`, - blocks: this.buildReviewBlocks(threadId, requestor, languages, deadlineDisplay), + blocks: this.buildReviewBlocks( + threadId, + requestor, + languages, + deadlineDisplay, + candidateTypeDisplay, + ), }; }, @@ -23,9 +30,10 @@ export const requestBuilder = { requestor: { id: string }, languages: string[], deadlineDisplay: string, + candidateTypeDisplay: string, ): Block[] { return [ - this.buildReviewSectionBlock(requestor, languages, deadlineDisplay), + this.buildReviewSectionBlock(requestor, languages, deadlineDisplay, candidateTypeDisplay), this.buildReviewActionsBlock(threadId), ]; }, @@ -34,6 +42,7 @@ export const requestBuilder = { requestor: { id: string }, languages: string[], deadlineDisplay: string, + candidateTypeDisplay: string, ): SectionBlock { return { block_id: BlockId.REVIEWER_DM_CONTEXT, @@ -45,7 +54,12 @@ export const requestBuilder = { requestor, )} has requested a HackerRank review done in the following languages:`, ul(...languages), + bold(`Candidate Type: ${candidateTypeDisplay}`), bold(`The review is needed by end of day ${deadlineDisplay}`), + compose( + bold('Test Information:'), + 'The test has 4 questions: 2 easy and 2 medium difficulty.\nSection 1 contains the easy questions, Section 2 contains the medium questions.\nCandidates should try to solve one problem from each section.\nThey have 70 minutes total to complete the test.', + ), ), }, };