Files
calendar/packages/lib/server/service/workflows.test.ts
T
+1
Carina WollendorferGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>amit@cal.com <samit91848@gmail.com>CarinaWolliAmit Sharmacoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>Udit TakkarBenny Joocal.comMorgan
e5f14c9316 feat: form submitted no event booked workflow trigger #2 (#23716)
* refactor: improve _scheduleWorkflowReminders readability and add missing booking trigger events

- Extract complex conditional logic into helper functions (isImmediateTrigger, isTimeBased, shouldProcessWorkflow)
- Add missing workflow trigger events with immediate execution logic
- Update test workflows to use different actions (EMAIL_ATTENDEE, SMS_ATTENDEE) for better differentiation
- Fix translation function mock in confirm.handler.test.ts using mockNoTranslations utility
- Maintain existing functionality while improving code maintainability

Co-Authored-By: amit@cal.com <samit91848@gmail.com>

* only show customt emplate for form triggers

* filter outside scheduleWorkflowReminder

* fix type check

* chore: add more tests

* test: add comprehensive unit tests for handleMarkNoShow with webhook and workflow coverage

- Create handleMarkNoShow.test.ts following confirm.handler.test.ts pattern
- Add expectBookingNoShowUpdatedWebhookToHaveBeenFired utility function
- Test both webhook and workflow triggers for BOOKING_NO_SHOW_UPDATED
- Cover attendee/host no-show scenarios, multiple attendees, and error cases
- All 6 unit tests pass with proper mocking of external dependencies

Co-Authored-By: amit@cal.com <samit91848@gmail.com>

* Revert "test: add comprehensive unit tests for handleMarkNoShow with webhook and workflow coverage"

This reverts commit 764299220279f0c012392dec24d3150246bfc4ad.

* fix: add new workflow triggers to api/v2

* update swagger docs

* fix: e2e

* fix type check

* fix tests, add test for before after events

* fix unit tests

* revert confirm.handler.test

* fix: unit tests

* dummy form variables

* add routing forms to active on dropdown

* add migration file

* Ui fixes for variables dropdown

* remove other translation keys

* review fixes

* allow routing forms for activeOn

* use repository function to get routing forms

* remove unnecessary code

* adjust logic in update handler

* add triggers to api v2

* remvoe unused file

* rename to getAcitveOnOptions handler

* remove routingFormOptions handler

* clean up getActiveOnOptions

* refactor WorkflowService

* remove logs

* remove unused

* fix: type check

* fix: missed before after events for recurring

* fix: calendarEvent handleMarkNoShow

* fix error message

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* don't query disabled routing forms

* create tasker function

* add tasker code

* move isFormTrigger function

* small adjustments + todo comments

* remove email to host action for form triggers

* throw trpc error if email to host is added as step

* fix dialog on how to use form responses as variables

* remove add variable dropdown for form triggers

* remove form workfows in event workflows tab

* improvements for workflow logic on form submission

* review fixes

* base setup for seperate schedule functions (evt and form)

* add missing BOOKING_PAID workflow trigger

* fix pathname

* fix: test for BOOKING_REQUESTED

* fix activeOn ids

* pass hideBranding and smsReminderNumber

* adjustments to reminderScheduler

* create empty scheduelForForm functions

* pass locale and timezone with form user

* pass formData instead of responses

* pass timeFormat and locale

* reusable function for email sending and reminder creation

* implement scheduleEmailReminderForForm

* remove added editor field from merge conflict

* don't support cal.ai action with form triggers

* throw bad request if form trigger and cal.ai is combined

* add tests for scheduleFormWorkflows

* add form submission tests

* remove form response varibe info

* clean up workflow actions

* fixes for getting template options

* pass triggerType to getAllWorkflows

* move reusable logic to scheduleSMSReminder

* add formdata to param type

* type fixes for text reminder managers

* implement scheduleSMSReminderForForm

* fix import

* fix isAuthorizedToAddActiveOnIds

* disble whatsapp action

* implement triggerFormSubmittedNoEventWorkflow

* code clean up

* Merge branch 'devin/1755107037-add-workflow-triggers' into feat/routing-form-workflow-triggers

* fix type errors

* remove async from getSubmitterEmail

* fix type errors

* revert cal.ai changes

* fix type error

* add sublogger

* code clean up

* fix type errors

* remove label for attendee whatsapp action

* code clean up

* fixes saving teams on org workflows

* fix type error

* code improvements for activeOn ids

* Revert "code improvements for activeOn ids"

This reverts commit 0a3590a4e2ce541b17d63483ad86ed458795a6a3.

* improve variable name

* fix unit tests

* small fixes

* type fixes

* remove unused translation keys

* fix merge conflict issues

* code clean up

* remove SMS action support

* remove more SMS code

* add missing imports

* set custom template for form action

* type fixes

* fix tasker endpoint

* fix duplicate check

* fix workfows.test.ts

* use repository funciton to getHideBranding

* code clean up

* fix hasDuplicateSubmission

* code clean up

* select only needed properties

* remove repository functions

* Revert "remove repository functions"

This reverts commit 7aa47b1c59c9abd7f964ebf26f746934c53a44f4.

* add scheduleWorkflows function

* Revert "add scheduleWorkflows function"

This reverts commit fe5db4fe3b65e2743c95475d585300cab98beed7.

* move type to /types

* Revert "move type to /types"

This reverts commit 91e0152154594b3772a801a426260068a8ccea54.

* revert changes causing type errors

* remove import

* remove unused import

* Revert "remove unused import"

This reverts commit 1916768c875ea5d0ac5598ccb8f9c796c5622dc9.

* revert changed from attempt to fix type errors

* pass object to gt all workflows

* fix isAuthorized check

* trigger filtering

* remove form submitted no event booked code

* remove form submitted no event from schema

* remove more code

* remove test

* fixes

* add getSubmitterEmail function

* add trigger

* small fixes

* add missing workflow DTOs

* small fixes

* use activeOnWithChildren

* fix active on when switching trigger type

* remove add variable dropdown

* add getAllWorkflowsFromRoutingForm to WorkflowService

* fix error caused by undefined evt

* fix type error

* fix type error

* fix tests

* code clean up

* final fixes and clean up

* remove console.log

* remove template text form from triggers

* add routing form repoditory function

* fix bug with key

* fix test

* add missing trigger in update-workflow.input.ts

* ForEvt and ForForm function for aiPhoneCallManager

* chore: add support for form workflows on api v2

* fixup! chore: add support for form workflows on api v2

* use only repository functions in update handler

* move all prisma queries from list.handler

* review suggestions

* chore: handle workflows api v2

* chore: handle workflows api v2, split in 2 endpoints

* fix workflow step creation

* remove connect agent and fixes types

* add type to workflow

* chore: use workflow type in apiv2 WorkflowsOutputService

* update worklfow type on update

* chore: use workflow type in apiv2 WorkflowsOutputService

* fix template body for torm trigger

* some UI fixes for email subject/body

* resetting email body when changing form triggers

* use type field to query workflows

* clean up all old active on values

* remove responseId from all funciton calls

* remove undefined from updateTemplate

* refactor: split routing form and event-type workflows code

* refactor: split routing form and event-type workflows code

* fix template  text when adding action

* chore: don't rename WorkflowActivationDto to avoid ci blocking

* refine update schedule to use only allowed actions

* fix type error

* don't allow whatsapp action with form trigger

* fix type error

* return early if activeOn array is empty

* fix: from step type in BaseFormWorkflowStepDto

* fixup! fix: from step type in BaseFormWorkflowStepDto

* api v2 updates

* move all prisma calls to repository (service/workflows.ts)

* use FORM_TRIGGER_WORKFLOW_EVENTS for form queries

* use userRepository

* use FORM_TRIGGER_WORKFLOW_EVENTS in isFormTrigger

* code clean up

* code clean up

* use repository functions in formSubmissionValidation.ts

* add back trpc  import

* fix agent repository functions

* remove unsued import

* fixes for offset api v2

* add missing responseId

* fix failing test

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: amit@cal.com <samit91848@gmail.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Amit Sharma <74371312+Amit91848@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Benny Joo <sldisek783@gmail.com>
Co-authored-by: cal.com <morgan@cal.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
2025-10-13 16:13:56 +00:00

210 lines
5.6 KiB
TypeScript

import { describe, expect, vi, beforeEach } from "vitest";
import { scheduleWorkflowReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler";
import { tasker } from "@calcom/features/tasker";
import { WorkflowTriggerEvents, WorkflowActions, WorkflowTemplates, TimeUnit } from "@calcom/prisma/enums";
import { test } from "@calcom/web/test/fixtures/fixtures";
import { WorkflowService } from "./workflows";
vi.mock("@calcom/features/ee/workflows/lib/reminders/reminderScheduler");
vi.mock("@calcom/features/tasker");
const mockScheduleWorkflowReminders = vi.mocked(scheduleWorkflowReminders);
const mockTasker = vi.mocked(tasker);
// Mock the getHideBranding function to return false
vi.mock("@calcom/features/profile/lib/hideBranding", () => ({
getHideBranding: vi.fn().mockResolvedValue(false),
}));
describe("WorkflowService.scheduleFormWorkflows", () => {
beforeEach(() => {
vi.clearAllMocks();
});
const mockForm = {
id: "form-123",
userId: 101,
teamId: null,
fields: [
{ type: "email", identifier: "email" },
{ type: "phone", identifier: "phone" },
],
user: {
email: "formowner@example.com",
timeFormat: 12,
locale: "en",
},
};
const mockResponses = {
email: {
value: "submitter@example.com",
response: "submitter@example.com",
},
phone: {
value: "+1234567890",
response: "+1234567890",
},
};
test("should call scheduleWorkflowReminders for FORM_SUBMITTED triggers with correct phone number", async () => {
const workflows = [
{
id: 1,
name: "Form Submitted",
userId: 101,
teamId: null,
trigger: WorkflowTriggerEvents.FORM_SUBMITTED,
time: null,
timeUnit: null,
steps: [
{
id: 1,
action: WorkflowActions.SMS_ATTENDEE,
sendTo: null,
reminderBody: "Thank you!",
emailSubject: "Form Received",
template: WorkflowTemplates.CUSTOM,
verifiedAt: new Date(),
includeCalendarEvent: false,
numberVerificationPending: false,
numberRequired: false,
},
],
},
];
await WorkflowService.scheduleFormWorkflows({
workflows,
responses: mockResponses,
form: mockForm,
});
expect(mockScheduleWorkflowReminders).toHaveBeenCalledWith({
smsReminderNumber: "+1234567890",
formData: {
responses: mockResponses,
user: { email: "formowner@example.com", timeFormat: 12, locale: "en" },
},
hideBranding: false,
workflows: [workflows[0]],
});
});
test("should create task for FORM_SUBMITTED_NO_EVENT triggers", async () => {
const workflows = [
{
id: 2,
name: "Form Follow-up",
userId: 101,
teamId: null,
trigger: WorkflowTriggerEvents.FORM_SUBMITTED_NO_EVENT,
time: 30,
timeUnit: TimeUnit.MINUTE,
steps: [
{
id: 2,
action: WorkflowActions.EMAIL_ATTENDEE,
sendTo: null,
reminderBody: "Follow up message",
emailSubject: "Follow Up",
template: WorkflowTemplates.CUSTOM,
verifiedAt: new Date(),
includeCalendarEvent: false,
numberVerificationPending: false,
numberRequired: false,
sender: null,
},
],
},
];
mockTasker.create.mockResolvedValue({ id: "task-123" });
await WorkflowService.scheduleFormWorkflows({
workflows,
responses: mockResponses,
responseId: 123,
form: mockForm,
});
expect(mockTasker.create).toHaveBeenCalledWith(
"triggerFormSubmittedNoEventWorkflow",
{
responseId: 123,
responses: mockResponses,
smsReminderNumber: "+1234567890",
hideBranding: false,
submittedAt: expect.any(Date),
form: {
id: "form-123",
userId: 101,
teamId: undefined,
user: {
email: "formowner@example.com",
timeFormat: 12,
locale: "en",
},
},
workflow: workflows[0],
},
{ scheduledAt: expect.any(Date) }
);
});
test("should handle forms without phone fields by passing null smsReminderNumber", async () => {
const formWithoutPhone = {
...mockForm,
fields: [
{ type: "email", identifier: "email" },
{ type: "text", identifier: "name" },
],
};
const workflows = [
{
id: 1,
name: "Form Submitted",
userId: 101,
teamId: null,
trigger: WorkflowTriggerEvents.FORM_SUBMITTED,
time: null,
timeUnit: null,
steps: [
{
id: 1,
action: WorkflowActions.EMAIL_ATTENDEE,
sendTo: null,
reminderBody: "Thank you!",
emailSubject: "Form Received",
template: WorkflowTemplates.CUSTOM,
verifiedAt: new Date(),
includeCalendarEvent: false,
numberVerificationPending: false,
numberRequired: false,
sender: null,
},
],
},
];
await WorkflowService.scheduleFormWorkflows({
workflows,
responses: mockResponses,
form: formWithoutPhone,
});
expect(mockScheduleWorkflowReminders).toHaveBeenCalledWith({
smsReminderNumber: null,
formData: {
responses: mockResponses,
user: { email: "formowner@example.com", timeFormat: 12, locale: "en" },
},
hideBranding: false,
workflows: [workflows[0]],
});
});
});